{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# numpy\n",
    "https://numpy.org/doc/1.16/\n",
    "* [创建数组](#创建数组) \n",
    "* [存取元素](#存取元素) \n",
    "* [数组间常用运算](#数组间常用运算) \n",
    "* [自定义ufunc函数](#自定义ufunc函数) \n",
    "* [广播](#广播) \n",
    "* [随机数](#随机数) \n",
    "* [统计](#统计) \n",
    "* [排序](#排序) \n",
    "* [分段函数](#分段函数) \n",
    "* [矩阵计算](#矩阵计算) \n",
    "* [掩码数组](#掩码数组) \n",
    "* [数组保存](#数组保存) \n",
    "* [相似度计算](#相似度计算) \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Platform : linux [win32/linux]\n",
      "Systerm  : 3.6.8 (default, Jan 14 2019, 11:02:34) \n",
      "[GCC 8.0.1 20180414 (experimental) [trunk revision 259383]] \n",
      "numpy Version: 1.16.4\n"
     ]
    }
   ],
   "source": [
    "import sys\n",
    "import numpy as np \n",
    "np.set_printoptions(precision=3)   # 设置 numpy 显示位数\n",
    "print('Platform : {} [win32/linux]'.format(sys.platform))  # 当前平台信息 \n",
    "print('Systerm  : {} '.format(sys.version))\n",
    "print('numpy Version: {}'.format(np.__version__))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## 创建数组"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "numpy 数据格式：\n",
      " {<class 'numpy.timedelta64'>, <class 'numpy.float16'>, <class 'numpy.uint8'>, <class 'numpy.int8'>, <class 'numpy.object_'>, <class 'numpy.datetime64'>, <class 'numpy.uint64'>, <class 'numpy.int64'>, <class 'numpy.void'>, <class 'numpy.complex256'>, <class 'numpy.float128'>, <class 'numpy.uint64'>, <class 'numpy.int64'>, <class 'numpy.str_'>, <class 'numpy.complex128'>, <class 'numpy.float64'>, <class 'numpy.uint32'>, <class 'numpy.int32'>, <class 'numpy.bytes_'>, <class 'numpy.complex64'>, <class 'numpy.float32'>, <class 'numpy.uint16'>, <class 'numpy.int16'>, <class 'numpy.bool_'>}\n",
      "\n",
      "a: \n",
      "value:  [1. 2. 3. 4.]\n",
      "shape:  (4,)\n",
      "dtype:  float32\n",
      "\n",
      "a.tolist()\n",
      " [1.0, 2.0, 3.0, 4.0]\n",
      "\n",
      "a.tostring()\n",
      " b'\\x00\\x00\\x80?\\x00\\x00\\x00@\\x00\\x00@@\\x00\\x00\\x80@'\n",
      "\n",
      "Reshape: \n",
      "value: \n",
      " [[1. 2.]\n",
      " [3. 4.]]\n",
      "shape:  (2, 2)\n",
      "dtype:  float32\n",
      "\n",
      "np.arange    :  [1.  1.2 1.4 1.6 1.8]\n",
      "np.linspace  :  [0.  0.2 0.4 0.6 0.8 1. ]\n",
      "np.logspace  :  [   1.   10.  100. 1000.]\n",
      "\n",
      "np.empty  :  \n",
      "[[-128   55  -77    2]\n",
      " [   0    0    0    0]\n",
      " [  96  -38   -5 -121]]\n",
      "\n",
      "np.zeros  :  \n",
      "[[0. 0. 0. 0.]\n",
      " [0. 0. 0. 0.]\n",
      " [0. 0. 0. 0.]]\n",
      "\n",
      "np.ones_like  :  \n",
      "[[1 1 1 1]\n",
      " [1 1 1 1]\n",
      " [1 1 1 1]]\n",
      "\n",
      "np.frombuffer : \n",
      "abaddf -> [ 97  98  97 100 100 102]\n",
      "\n",
      "np.fromfunction  lambda i,j: (i+1)*(j+1):\n",
      "[[ 1  2  3  4  5  6  7  8  9]\n",
      " [ 2  4  6  8 10 12 14 16 18]\n",
      " [ 3  6  9 12 15 18 21 24 27]\n",
      " [ 4  8 12 16 20 24 28 32 36]\n",
      " [ 5 10 15 20 25 30 35 40 45]\n",
      " [ 6 12 18 24 30 36 42 48 54]\n",
      " [ 7 14 21 28 35 42 49 56 63]\n",
      " [ 8 16 24 32 40 48 56 64 72]\n",
      " [ 9 18 27 36 45 54 63 72 81]]\n"
     ]
    }
   ],
   "source": [
    "# 查看帮助文档\n",
    "# np.array?\n",
    "\n",
    "print('numpy 数据格式：\\n', set(np.typeDict.values()))\n",
    "\n",
    "# 创建 numpy array\n",
    "a = np.array([1,2,3,4], dtype=np.float32)\n",
    "print('\\na: ')\n",
    "print('value: ', a)\n",
    "print('shape: ', a.shape)\n",
    "print('dtype: ', a.dtype)\n",
    "# 转换\n",
    "print('\\na.tolist()\\n', a.tolist())\n",
    "print('\\na.tostring()\\n', a.tostring())\n",
    "\n",
    "# reshape 更换大小\n",
    "b = a.reshape((2,-1))\n",
    "print('\\nReshape: ')\n",
    "print('value: \\n', b)\n",
    "print('shape: ', b.shape)\n",
    "print('dtype: ', b.dtype)\n",
    "\n",
    "print('\\nnp.arange    :  {}'.format(np.arange(1, 2, 0.2)))  # arange([start,] stop[, step,], dtype=None)  不包括 stop值\n",
    "print('np.linspace  :  {}'.format(np.linspace(0,1,6)))  # np.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis=0) \n",
    "print('np.logspace  :  {}'.format(np.logspace(0,3,4)))  # np.logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None, axis=0)\n",
    "\n",
    "print('\\nnp.empty  :  \\n{}'.format(np.empty(shape=(3,4), dtype=np.int8)))  # empty(shape, dtype=float, order='C')\n",
    "print('\\nnp.zeros  :  \\n{}'.format(np.zeros(shape=(3,4), dtype=np.float)))  # zeros(shape, dtype=float, order='C')\n",
    "print('\\nnp.ones_like  :  \\n{}'.format(np.ones_like(np.empty(shape=(3,4), dtype=np.int8), dtype=np.int8)))  # np.ones_like(a, dtype=None, order='K', subok=True)\n",
    "\n",
    "# 从字符串创建\n",
    "print('\\nnp.frombuffer : \\n{} -> {}'.format('abaddf', np.frombuffer('abaddf'.encode('utf-8'), dtype=np.uint8)))\n",
    "# 从自定义函数生成\n",
    "print('\\nnp.fromfunction  lambda i,j: (i+1)*(j+1):\\n{}'.format(np.fromfunction(lambda i,j: (i+1)*(j+1), (9,9), dtype=np.uint8)))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## 存取元素"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[ 1  2  3  4  5  6  7  8  9 10]\n",
      " [11 12 13 14 15 16 17 18 19 20]\n",
      " [21 22 23 24 25 26 27 28 29 30]\n",
      " [31 32 33 34 35 36 37 38 39 40]\n",
      " [41 42 43 44 45 46 47 48 49 50]]\n",
      "\n",
      "a[3]:\n",
      "[31 32 33 34 35 36 37 38 39 40]\n",
      "\n",
      "a[:,3]:\n",
      "[ 4 14 24 34 44]\n",
      "\n",
      "a[2:4]:\n",
      "[[21 22 23 24 25 26 27 28 29 30]\n",
      " [31 32 33 34 35 36 37 38 39 40]]\n",
      "\n",
      "a[:,2:4]:\n",
      "[[ 3  4]\n",
      " [13 14]\n",
      " [23 24]\n",
      " [33 34]\n",
      " [43 44]]\n",
      "\n",
      "a[:-1]:\n",
      "[[41 42 43 44 45 46 47 48 49 50]\n",
      " [31 32 33 34 35 36 37 38 39 40]\n",
      " [21 22 23 24 25 26 27 28 29 30]\n",
      " [11 12 13 14 15 16 17 18 19 20]\n",
      " [ 1  2  3  4  5  6  7  8  9 10]]\n",
      "\n",
      "a[:,:-1]:\n",
      "[[10  9  8  7  6  5  4  3  2  1]\n",
      " [20 19 18 17 16 15 14 13 12 11]\n",
      " [30 29 28 27 26 25 24 23 22 21]\n",
      " [40 39 38 37 36 35 34 33 32 31]\n",
      " [50 49 48 47 46 45 44 43 42 41]]\n",
      "\n",
      "a[:2,:3]:\n",
      "[[ 1  2  3]\n",
      " [11 12 13]]\n",
      "\n",
      "a[:2,][:,:3]:\n",
      "[[ 1  2  3]\n",
      " [11 12 13]]\n",
      "\n",
      "a[[0,1,3],[5,2,4]]:\n",
      "[ 6 13 35]\n",
      "\n",
      "a[[0,1,3]][:,[5,2,4]]:\n",
      "[[ 6  3  5]\n",
      " [16 13 15]\n",
      " [36 33 35]]\n",
      "\n",
      "a[np.arange(5)>=3]:\n",
      "[[31 32 33 34 35 36 37 38 39 40]\n",
      " [41 42 43 44 45 46 47 48 49 50]]\n",
      "\n",
      "\n",
      "a[3] += 1000:\n",
      "[[   1    2    3    4    5    6    7    8    9   10]\n",
      " [  11   12   13   14   15   16   17   18   19   20]\n",
      " [  21   22   23   24   25   26   27   28   29   30]\n",
      " [1031 1032 1033 1034 1035 1036 1037 1038 1039 1040]\n",
      " [  41   42   43   44   45   46   47   48   49   50]]\n",
      "\n",
      "切片视图:\n",
      "[[   1    2    3    4    5  600  700  800    9   10]\n",
      " [  11   12   13   14   15   16   17   18   19   20]\n",
      " [  21   22   23   24   25 2600 2700 2800   29   30]\n",
      " [1031 1032 1033 1034 1035 1036 1037 1038 1039 1040]\n",
      " [  41   42   43   44   45 4600 4700 4800   49   50]]\n",
      "\n",
      "a[::2,5:8]:\n",
      "[[ 600  700  800]\n",
      " [2600 2700 2800]\n",
      " [4600 4700 4800]]\n",
      "(160, 8)\n",
      "  C_CONTIGUOUS : False\n",
      "  F_CONTIGUOUS : False\n",
      "  OWNDATA : False\n",
      "  WRITEABLE : True\n",
      "  ALIGNED : True\n",
      "  WRITEBACKIFCOPY : False\n",
      "  UPDATEIFCOPY : False\n",
      "\n",
      "列表复制:\n",
      "[[   1    2    3    4    5  600  700  800    9   10]\n",
      " [  11   12   13   14   15   16   17   18   19   20]\n",
      " [  21   22   23   24   25 2600 2700 2800   29   30]\n",
      " [1031 1032 1033 1034 1035 1036 1037 1038 1039 1040]\n",
      " [  41   42   43   44   45 4600 4700 4800   49   50]]\n",
      "\n",
      "a[[0,1,3],[5,2,4]]:\n",
      "[10600 10013 11035]\n",
      "(8,)\n",
      "  C_CONTIGUOUS : True\n",
      "  F_CONTIGUOUS : True\n",
      "  OWNDATA : True\n",
      "  WRITEABLE : True\n",
      "  ALIGNED : True\n",
      "  WRITEBACKIFCOPY : False\n",
      "  UPDATEIFCOPY : False\n"
     ]
    }
   ],
   "source": [
    "a = np.arange(1, 51).reshape(5,10)\n",
    "print(a)\n",
    "\n",
    "print('\\n{:s}:\\n{}'.format('a[3]', a[3]))\n",
    "print('\\n{:s}:\\n{}'.format('a[:,3]', a[:,3]))\n",
    "print('\\n{:s}:\\n{}'.format('a[2:4]', a[2:4]))\n",
    "print('\\n{:s}:\\n{}'.format('a[:,2:4]', a[:,2:4]))\n",
    "print('\\n{:s}:\\n{}'.format('a[:-1]', a[::-1]))      # 逆序\n",
    "print('\\n{:s}:\\n{}'.format('a[:,:-1]', a[:,::-1]))  # 逆序\n",
    "print('\\n{:s}:\\n{}'.format('a[:2,:3]', a[:2,:3]))  # 切片索引\n",
    "print('\\n{:s}:\\n{}'.format('a[:2,][:,:3]', a[:2,][:,:3]))  # 切片索引\n",
    "\n",
    "print('\\n{:s}:\\n{}'.format('a[[0,1,3],[5,2,4]]', a[[0,1,3],[5,2,4]])) # 列表索引\n",
    "print('\\n{:s}:\\n{}'.format('a[[0,1,3]][:,[5,2,4]]', a[[0,1,3]][:,[5,2,4]])) # 列表索引\n",
    "\n",
    "print('\\n{:s}:\\n{}'.format('a[np.arange(5)>=3]', a[np.arange(5)>=3]))  # 布尔数组\n",
    "\n",
    "# 修改数据  \n",
    "# ### 切片方式 为原数据视图，共享数据存储区域，修改时原数据对应被修改\n",
    "a[3] += 1000\n",
    "print('\\n\\n{:s}:\\n{}'.format('a[3] += 1000', a))\n",
    "b = a[::2,5:8]\n",
    "b *= 100\n",
    "print('\\n{:s}:\\n{}'.format('切片视图', a))\n",
    "print('\\n{:s}:\\n{}'.format('a[::2,5:8]', b))\n",
    "print(b.strides) # 每个轴上相邻两个元素地址差\n",
    "print(b.flags)\n",
    "\n",
    "# ### 列表、布尔式 复制原数据到另一存储空间，修改时原数据不被修改\n",
    "b = a[[0,1,3],[5,2,4]] \n",
    "b += 10000\n",
    "print('\\n{:s}:\\n{}'.format('列表复制', a))\n",
    "print('\\n{:s}:\\n{}'.format('a[[0,1,3],[5,2,4]]', b))\n",
    "print(b.strides) # 每个轴上相邻两个元素地址差\n",
    "print(b.flags)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## 数组间常用运算"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Raw  a:\n",
      "[[0.    0.571 1.142 1.714]\n",
      " [2.285 2.856 3.427 3.998]\n",
      " [4.57  5.141 5.712 6.283]]\n",
      "\n",
      "Raw  b:\n",
      "[[ 0.  1.  2.  3.]\n",
      " [ 4.  5.  6.  7.]\n",
      " [ 8.  9. 10. 11.]]\n",
      "\n",
      "Raw  c:\n",
      "[[4. 4. 4. 4.]\n",
      " [4. 4. 4. 4.]\n",
      " [4. 4. 4. 4.]]\n",
      "\n",
      "np.sin(a):\n",
      "[[ 0.000e+00  5.406e-01  9.096e-01  9.898e-01]\n",
      " [ 7.557e-01  2.817e-01 -2.817e-01 -7.557e-01]\n",
      " [-9.898e-01 -9.096e-01 -5.406e-01 -2.449e-16]]\n",
      "\n",
      "np.add(b, c):\n",
      "[[ 4.  5.  6.  7.]\n",
      " [ 8.  9. 10. 11.]\n",
      " [12. 13. 14. 15.]]\n",
      "\n",
      "b-c:\n",
      "[[-4. -3. -2. -1.]\n",
      " [ 0.  1.  2.  3.]\n",
      " [ 4.  5.  6.  7.]]\n",
      "\n",
      "b*c:\n",
      "[[ 0.  4.  8. 12.]\n",
      " [16. 20. 24. 28.]\n",
      " [32. 36. 40. 44.]]\n",
      "\n",
      "b/c:\n",
      "[[0.   0.25 0.5  0.75]\n",
      " [1.   1.25 1.5  1.75]\n",
      " [2.   2.25 2.5  2.75]]\n",
      "\n",
      "np.divide(b, c):\n",
      "[[0.   0.25 0.5  0.75]\n",
      " [1.   1.25 1.5  1.75]\n",
      " [2.   2.25 2.5  2.75]]\n",
      "\n",
      "np.true_divide(b, c):\n",
      "[[0.   0.25 0.5  0.75]\n",
      " [1.   1.25 1.5  1.75]\n",
      " [2.   2.25 2.5  2.75]]\n",
      "\n",
      "b//c:\n",
      "[[0. 0. 0. 0.]\n",
      " [1. 1. 1. 1.]\n",
      " [2. 2. 2. 2.]]\n",
      "\n",
      "b%c:\n",
      "[[0. 1. 2. 3.]\n",
      " [0. 1. 2. 3.]\n",
      " [0. 1. 2. 3.]]\n",
      "\n",
      "b**c:\n",
      "[[0.000e+00 1.000e+00 1.600e+01 8.100e+01]\n",
      " [2.560e+02 6.250e+02 1.296e+03 2.401e+03]\n",
      " [4.096e+03 6.561e+03 1.000e+04 1.464e+04]]\n",
      "\n",
      "a > c:\n",
      "[[False False False False]\n",
      " [False False False False]\n",
      " [ True  True  True  True]]\n",
      "\n",
      "~(a > c):\n",
      "[[ True  True  True  True]\n",
      " [ True  True  True  True]\n",
      " [False False False False]]\n",
      "\n",
      "(a > c).all(axis=0):\n",
      "[False False False False]\n",
      "\n",
      "(a > c).any(axis=1):\n",
      "[False False  True]\n",
      "\n",
      "b > c:\n",
      "[[False False False False]\n",
      " [False  True  True  True]\n",
      " [ True  True  True  True]]\n",
      "\n",
      "(a > c) & (b > c):\n",
      "[[False False False False]\n",
      " [False False False False]\n",
      " [ True  True  True  True]]\n",
      "\n",
      "np.logical_and(a > c, b > c):\n",
      "[[False False False False]\n",
      " [False False False False]\n",
      " [ True  True  True  True]]\n",
      "\n",
      "np.logical_xor(a > c, b > c):\n",
      "[[False False False False]\n",
      " [False  True  True  True]\n",
      " [False False False False]]\n",
      "\n",
      "np.allclose(b, b+1e-07):\n",
      "False\n",
      "\n",
      "np.allclose(b, b+1e-08):\n",
      "True\n"
     ]
    }
   ],
   "source": [
    "a = np.linspace(0, 2*np.pi, 12).reshape(3, -1)\n",
    "b = np.arange(0, 12, dtype=np.float).reshape(3, -1)\n",
    "c = np.ones_like(b, dtype=np.float) * 4\n",
    "\n",
    "print('\\n{}:\\n{}'.format('Raw  a', a))\n",
    "print('\\n{}:\\n{}'.format('Raw  b', b))\n",
    "print('\\n{}:\\n{}'.format('Raw  c', c))\n",
    "\n",
    "\n",
    "print('\\n{}:\\n{}'.format('np.sin(a)', np.sin(a)))\n",
    "print('\\n{}:\\n{}'.format('np.add(b, c)', np.add(b, c)))\n",
    "print('\\n{}:\\n{}'.format('b-c', b-c))\n",
    "print('\\n{}:\\n{}'.format('b*c', b*c))\n",
    "print('\\n{}:\\n{}'.format('b/c', b/c))\n",
    "print('\\n{}:\\n{}'.format('np.divide(b, c)', np.divide(b, c)))\n",
    "print('\\n{}:\\n{}'.format('np.true_divide(b, c)', np.true_divide(b, c)))\n",
    "print('\\n{}:\\n{}'.format('b//c', b//c))  # np.floor_divide(b, c)  # 总是对返回值取整\n",
    "print('\\n{}:\\n{}'.format('b%c', b%c))   # np.mod(b, c)\n",
    "print('\\n{}:\\n{}'.format('b**c', b**c))  \n",
    "\n",
    "print('\\n{}:\\n{}'.format('a > c', a > c))\n",
    "print('\\n{}:\\n{}'.format('~(a > c)', ~(a > c)))\n",
    "print('\\n{}:\\n{}'.format('(a > c).all(axis=0)', (a > c).all(axis=0)))\n",
    "print('\\n{}:\\n{}'.format('(a > c).any(axis=1)', (a > c).any(axis=1)))\n",
    "print('\\n{}:\\n{}'.format('b > c', b > c))\n",
    "print('\\n{}:\\n{}'.format('(a > c) & (b > c)', (a > c) & (b > c)))\n",
    "print('\\n{}:\\n{}'.format('np.logical_and(a > c, b > c)', np.logical_and(a > c, b > c)))\n",
    "print('\\n{}:\\n{}'.format('np.logical_xor(a > c, b > c)', np.logical_xor(a > c, b > c)))\n",
    "print('\\n{}:\\n{}'.format('np.allclose(b, b+1e-07)', np.allclose(b, b+1e-07)))  # allclose方法，比较两个array是不是每一元素都相等，默认在1e-07的误差范围内\n",
    "print('\\n{}:\\n{}'.format('np.allclose(b, b+1e-08)', np.allclose(b, b+1e-08)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "### 自定义ufunc函数\n",
    "通过 np.frompyfunc(func, nin, nout) 可以将计算单个值的函数转换为一个能对数组中每个元素进行计算的 ufunc 函数   \n",
    "func 是计算单个元素的函数，nin 是func 输入参数的个数，nout 是func 返回值的个数\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 自定义三角波函数\n",
    "def triangle_wave(x, c, c0, hc):\n",
    "    x = x -int(x)  # 周期为 1，只取小数部分进行计算\n",
    "    if x >= c:\n",
    "        r =0.0\n",
    "    elif x < c0:\n",
    "        r = x/c0*hc\n",
    "    else:\n",
    "        r = (c-x)/(c-c0)*hc\n",
    "    return r"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAD4CAYAAAD8Zh1EAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjAsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+17YcXAAAgAElEQVR4nO3dfXRU93ng8e+j0YxmJEC8SIAQQoKATTAJMVYwYG/iFvcYOyl2gtfxK3g322y3Tbc97ek56cmebE/2j33p7nZPT7MvbrfHEjYmbnBrkoLTmLrHG2t4kVwwr7Yx1kUIISQBQghJI8389o8ZifEgaUbSvXNn7jyfczgezdzRPL7cefjd39sjxhiUUkrlvyK3A1BKKWUPTehKKeURmtCVUsojNKErpZRHaEJXSimPKHbrgysqKkxdXZ1bH6+UUnmppaWl2xhTOd5rriX0uro6mpub3fp4pZTKSyJiTfSadrkopZRHaEJXSimP0ISulFIeoQldKaU8QhO6Ukp5RNqELiJ/JSJXROTkBK+LiPyZiJwTkQ9EZL39YSqllEonkxb6y8DWSV5/FFiV+PMd4H/NPCyllFJTlTahG2PeBa5OcsjjQKOJOwTMFZEquwJUzuofGiEa0y2UlTPOXr7Be+e63Q6jYNjRh14NtCX9fDHx3B1E5Dsi0iwizV1dXTZ8tJqJvsFh7vn3P+e//f2HboeiPMgYw+/tOcb3/+aE26EUjKwOihpjXjLG1Btj6isrx125qrLojffbAXi9uS3NkUpN3ZFPr3L2ch83h0bcDqVg2JHQ24GapJ+XJp5TOSwWMzSEWwGYWxpwNRblTY3h+Ar1/qGoy5EUDjsS+j5gR2K2y0ag1xjTYcPvVQ5675Nuznf1UyQwENEvnLJXR+8Ab526TElxEQPDUWI6TpMVmUxbfA0IA3eLyEUR+baI/KaI/GbikP3AeeAc8BfAbzkWrbJNQ5PFgrIA29cvZXBYE7qy1+7DF4gZw5P3LQVgcESvsWxIu9uiMeaZNK8b4Ldti0g5ru3qLQ6e7eS3H1rJ4HCUAU3oykZDI1FeO3KBLasXcvfi2QDcikQpDbi2uWvB0JWiBeiVQxZFIjy3cRmhgI+B4Sjxf5eVmrn9Jzrovhlhx6Y6Qn4foN162aIJvcAMRKLsOdrGI/csoqo8RNDvwxgYGom5HZryiIYmixUVZTy4soKyknirvD+iM12yQRN6gfnp8Uv0DgyzY1MdwFgLSvvRlR2Ot13nWNt1dmyqpahICAXi19ctbaFnhSb0AmKM4eWmVu5eNJv7l88HGPvCaT+6skNDuJWygI/ticHQUu1yySpN6AWkxbrG6Y4b7Nxch4gAaB+nsk3PzSF+dryDb65fyuygH2BsIFRb6NmhCb2ANIQtZgeLeeLeJWPPBf3aQlf22HO0jUg0xs7NtWPP3e5y0T70bNCEXiA6bwxy4EQHT9XXfGb62OgXTvvQ1UyMRGO8esjigZULWLlw9tjzpdqHnlWa0AvE7sMXiBrDCxtrP/P87S4XneWipu/tM51c6h0cG2wfVaZdLlmlCb0AREZi7D5ygYfuqqSuouwzr4W0y0XZoKHJonpuiIc/v+gzz48NumuXS1ZoQi8AB0520NU3xI7NdXe8FgrELwFN6Gq6PursI3y+h+c31uIrks+85vcJviJhcFjvALNBE3oBaAxb1C0o5aur7tyyeHRQdFBvidU0NTS1Eigu4ltfrrnjNREhmNigSzlPE7rHnWzvpcW6xgub6ihKaT2BdrmomekdGOaN99t5fN0S5peNvw3z6PYSynma0D2uoamVkN83tutdKl1YpGZib8tFBoaj7BynO29U0O/TO8As0YTuYdf6I7x5/BLfXF9Necg/7jHBYl1YpKYnFjM0hltZv2wua6vLJzwu5NcWerZoQvewHze3ERmJ3TGVLFlRkRAoLtJ56GrK3v24i9aeW5O2ziF+F6jXV3ZoQveoaMywK2yxccX8sT2pJxLy6xdOTV1j2KJiVgmPrq2a9LigttCzRhO6Rx0800n79QF2TtI6H6W3xGqqrJ5+3vnwCs/ev4xA8eRpJJ7QddpiNmhC96jGsEVVeZBfW7Mo7bHxWQj6hVOZ2xW28Inw3P3L0h4b8hfpoGiWaEL3oHNX+vjluW6e31hLsS/9X3HQ79NBUZWxW5ERXm9uY+vaxSyaE0x7vN4BZo8mdA9qDFsEfOMv9BhPyK+Doipzf/tPl7gxOJJ2MHSUzkPPHk3oHtM3OMzelot8fV0VFbNKMnqPfuFUpoyJT1X8fNUc6mvnZfQenYeePZrQPeaN99vpj0QzGgwdFdIuF5WhI59e5ezlPl7cXDtWJCWdkN/H4IheX9mgCd1DYjFDQ7iVdTVzWVczN+P3BXXaospQY9iiPORn27rqjN8T8vsYjhqGozrw7jRN6B7y3ifdnO/q58XNtekPTqKDVioTHb0DvHXqMt/6cs3YlhGZCGoh8qzRhO4hDU0WC8oCPPaFyRd6pNI+dJWJ3YcvEBunSEo6Qd0vKGs0oXtE29VbHDzbyTMbllFSnHnrCbQPXaU3NBLltSMX2LJ6ITXzS6f03tDYFs3a5eI0Tege8cohiyIRntuYfqFHqqDfx9BIjFjMOBCZ8oL9JzrovhmZdF+giegWzdmjCd0DBiJR9hxt45F7FlFVHpry+8cKRetMBDWBhiaLFZVlPLiyYsrvHa2KpX3oztOE7gH7jrfTOzA8rdYTJBeK1i+cutPxtusca7vOjo214xZJSSeoLfSs0YSe54wxNDRZ3L1oNvcvnz+t36G3xGoyDeFWygI+tk9QJCUdTejZk1FCF5GtIvKhiJwTke+N8/oyEXlHRP5JRD4QkcfsD1WNp8W6xumOG+zcXJfxQo9Uo7MQ9JZYpeq5OcTPjnfwzfVLmR0cv0hKOiGtW5s1aRO6iPiAHwGPAmuAZ0RkTcph/w543RhzL/A08D/tDlSNryFsMTtYzBP3Lpn277jd5aKzENRn7TnaRiQaY+cU1zYk0zvA7Mmkhb4BOGeMOW+MiQB7gMdTjjHAnMTjcuCSfSGqiXTeGOTAiQ6eqq+hNFA87d+jXzg1npFojFcPWTywcgErF05eJGUyWrc2ezJJ6NVAW9LPFxPPJftj4HkRuQjsB37HlujUpHYfvkB0Ggs9Uo3OQtAvnEr29plOLvUOTnuwfVRQB92zxq5B0WeAl40xS4HHgF0icsfvFpHviEiziDR3dXXZ9NGFKTISY/eRCzx0VyV1FWUz+l36hVPjaWiyqJ4b4uHPpy+SMpnRO8ChEe3Sc1omCb0dSN5Ye2niuWTfBl4HMMaEgSBwx4RVY8xLxph6Y0x9ZWXl9CJWABw42UFX3xA7MtyTejIh3WtDpfios4/w+R6e31iLbxpTFZP5fYKvSLTBkAWZJPSjwCoRWS4iAeKDnvtSjrkAbAEQkc8TT+jaBHdQY9iibkEpX101838YtY9TpWpoaiVQnHmRlMmICMHiIr2+siBtQjfGjADfBX4OnCE+m+WUiPxQRLYlDvsD4DdE5DjwGvCiMUbXkTvkZHsvLdY1XthUN62FHql0YZFK1jswzBvvt/P4uiXMLwvY8jt1A7jsyGhqhDFmP/HBzuTnfpD0+DTwgL2hqYk0NLUS8vt4cpoLPVLpwg+VbG/LRQaGoxmXmMuEVi3KDl0pmmeu9Ud48/glvrm+mvLQ9BZ6pCopLkJE+9BVvEhKY7iV9cvmsra63Lbfq3vuZ4cm9Dzz4+Y2IiOxGU8lSyYiuoWuAuDdj7to7blla+sc4l0u2mBwnib0PBKNGXaFLTaumM/di6e/0GM82oJSEB9sr5hVwqNrp1YkJZ2gXl9ZoQk9jxw800n79QFetLn1BKN1RXWecCGzevp558MrPHv/MgLF9qaGeELX68tpmtDzSGPYoqo8OOOFHuPRW2K1K2zhE+G5+6deJCWdkL9IB0WzQBN6njh3pY9fnuvm+Y21FPvs/2vTLpfCdisywuvNbWxdu5hFc4K2/369vrJDE3qeaAxbBHz2LPQYjw6KFrY3j13ixuCI7YOho3QeenZoQs8DfYPD7G25yNfXVVExq8SRzwjqF65gxYuktPL5qjnU185z5DN0Hnp2aELPA2+8305/JMpOG6cqpgoWF2kfeoE68ulVzl7u48XNtdMukpJOyO/TmrVZoAk9x8VihoZwK+tq5rKuZq5jn6O3xIWrMWxRHvKzbV3qrtj2Cfl9DEcNw1Gd6eIkTeg57r1Pujnf1c+LM6gYkwntQy9MHb0DvHXqMt/6cs3YJm1OCOqOnlmhCT3HNTRZLCgL8NgX7F3okUoXfhSm3YcvELOhSEo6Qd3RMys0oeewtqu3OHi2k2c2LKOk2LnWE+g89EI0NBLltSMX2LJ6ITXzSx39rNuForXLxUma0HPYK4csikR4bqP9Cz1SaR9n4dl/ooPumxFb9wWaiNatzQ5N6DlqIBJlz9E2HrlnEVXlIcc/T6sWFZ6GJosVlWU8uPKO4mK2G61bq9eXszSh56h9x9vpHRjOSusJtI+z0Bxvu86xtuvs2FhrS5GUdHTP/ezQhJ6D4gs9LFYvns39y+dn5TO1j7OwNIRbKQv42G5TkZR0NKFnhyb0HNRiXeN0xw12bKpzbKFHKu3jLBw9N4f42fEOtt+3lNlBe4qkpHO7waDXl5M0oeegl5tamRMs5ol7l2TtM0f7ODWhe9+eo21EojF2bHJ2qmIybTBkhyb0HNN5Y5C3Tl7mqfoaSgMZlXy1RVALRReEkWiMVw9ZPLByASsX2lskZTIhHaPJCk3oOWb34QtEjeF5hxd6pNJZLoXh7TOdXOoddHRfoPHcXimqYzRO0oSeQyIjMXYfucBDd1VSV1GW1c/WFlRhaGiyqJ4bYosDRVImow2G7NCEnkMOnOygq2+IHQ7tST2ZkHa5eN5HnX2Ez/fw/MZafFmYqpjM7xN8RaLXl8M0oeeQxrBF3YJSvrqqMuufrYNW3tfQ1Eqg2LkiKZMREYLFRXp9OUwTeo442d5Li3WNFzbVZWWhR6rRhUV6S+xNvQPDvPF+O4+vW8L8soArMegWzc7ThJ4jGppaCfl9PJmlhR6ptMvF2/a2XGRgOOpYiblMaNUi52lCzwHX+iO8efwS31xfTXkoOws9Uvl9RRQXibagPCgWMzSGW1m/bC5rq8tdi0MLRTtPE3oO+HFzG5GRWNb2bZmIfuG86d2Pu2jtueVq6xx0i+Zs0ITusmjMsCtssXHFfO5enL2FHuMJ6hfOkxrDFhWzSnh0rbNFUtLRIirO04TusoNnOmm/PsCLLreeIFHIVxd+eIrV0887H17h2fuXESh29+seT+h6fTlJE7rLGsMWVeVBHs7yQo/xaF1R79kVtvCJ8Nz9zhdJSSfkL9JBUYdllNBFZKuIfCgi50TkexMc85SInBaRUyKy294wvenclT5+ea6b5zfWUuxz/9/WoE4r85RbkRFeb25j69rFLJoTdDscHaPJgrS7P4mID/gR8GvAReCoiOwzxpxOOmYV8EfAA8aYayKy0KmAvaQxbBHwFfG0Cws9xhPy68IPL3nz2CVuDI64Phg6SuehOy+TZuEG4Jwx5rwxJgLsAR5POeY3gB8ZY64BGGOu2Bum9/QNDrO35SJfX1fFglklbocDjPah6xfOC+JFUlpZUzWH+tp5bocDJOah6/XlqEwSejXQlvTzxcRzye4C7hKR90TkkIhsHe8Xich3RKRZRJq7urqmF7FH7G25SH8kmvVd7yYTCmgfulcc+fQqZy/3sXNzbdaKpKSjDQbn2dVxWwysAh4CngH+QkTmph5kjHnJGFNvjKmvrMz+fiW5Ir7Qw2JdzVzW1dxxmlyj08q8ozFsUR7ys21datvLPSG/j+GoYTiqM12ckklCbweSO3mXJp5LdhHYZ4wZNsZ8CnxEPMGrcbz3STfnu/t5cXN29zxPR1tQ3tDRO8Bbpy7z9JdrxrZFzgVB3ULXcZkk9KPAKhFZLiIB4GlgX8oxf0u8dY6IVBDvgjlvY5ye0tBksaAswGNfcHehRyqdtugNuw9fIOZCkZR0grrnvuPSJnRjzAjwXeDnwBngdWPMKRH5oYhsSxz2c6BHRE4D7wB/aIzpcSrofNZ29RYHz3byzIZllBTnTusJbs9CMMa4HYqapqGRKK8ducCW1QupmV/qdjifcbtQtHa5OCWjopXGmP3A/pTnfpD02AC/n/ijJvHKIYsiEZ7b6P5Cj1RBv4+YgUg0lnP/2KjM7D/RQffNiOv7Ao1H99x3nvurWQrIQCTKnqNtPHLPIqrKQ26Hc4egtqDyXkOTxYrKMh5cWeF2KHcIBeLpRvvQnaMJPYv2HW+nd2A4J1tPoC2ofHe87TrH2q6zY2OtK0VS0gnq9eU4TehZEl/oYbF68WzuXz7f7XDGNdqC0i9cfmoMW5QFfGx3qUhKOprQnacJPUtarGuc7rjBjk11ObPQI5VWLcpfPTeH+OkHl9h+31JmB90pkpLO7UFRvb6cogk9S15uamVOsJgn7l3idigT0hZU/tpzdLRISm5NVUymXXrO04SeBZ03Bnnr5GWeqq+hNJDRxCJXhHThR14aicZ49ZDFAysXsHKhu0VSJhPSeeiO04SeBbsPXyCagws9Uo194fSWOK+8faaTS72DObUv0HhurxTVWVRO0YTusMhIjN1HLvDQXZXUVZS5Hc6k9JY4PzU0WVTPDbElB4qkTEbvAJ2nCd1hB0520NU3lDN7Uk9G+9Dzz0edfYTP9/D8xlp8OThVMZnfJ/iKRO8AHaQJ3WGNYYu6BaV8ZVXu7y452uWiLaj80dDUSklx7hRJmYyIECzWIipO0oTuoJPtvbRY13hhU11OLvRIpdMW80vvwDBvvN/OtnVLmFcWcDucjGjVImdpQndQQ1MrIb+PJ3N0oUcq7XLJL3tbLjIwHM2L7rxRQb9P56E7SBO6Q671R3jz+CW+ub6a8lBuLvRI5SsSAnpLnBfiRVJaua92Hmury90OJ2Mhv4/BEb2+nKIJ3SE/bh5d6FHndihTEtIWVF549+MuWntu5fRCovFomUNnaUJ3QDRm2BW22LhiPncvzt2FHuMJaRm6vNAYtqiYVcKja3OrSEo6WubQWZrQHXDwTCft1wd4MY/6NkeFAj5d+JHjrJ5+3vnwCs/ev4xAcX59heMJXa8vp+TX1ZAnGsMWVeVBHs7xhR7j0RZU7tsVtvCJ8Nz9uVckJZ2Qv0i79BykCd1m56708ctz3Ty/sZZiX/6d3pC/SOeh57BbkRFeb25j69rFLJoTdDucKdMuPWflX8bJcY1hi4AvPxZ6jEcHrXLbm8cucWNwJK+mKibTeejO0oRuo77BYfa2XOTr66pYMKvE7XCmRVtQuSteJKWVNVVzqK+d53Y40xL0+/QO0EGa0G20t+Ui/ZFozu96NxntQ89dRz69ytnLfezcXJuzRVLSCWlCd5QmdJvEF3pYfKlmLutq5rodzrTpPPTc1Ri2KA/52bau2u1Qpi3k9zEcNQxHdaaLEzSh2+S9T7o5393Pzs35tdAjlfZx5qaO3gHeOnWZp79cM7aJWj4K6ha6jtKEbpOGplYqZgV47Av5tdAjlfah56bdhy8Qy4MiKekEtWqRozSh26Dt6i0Onr3CMxuWUVKcv60nGB20ihGLGbdDUQlDI1FeO3KBLasXUjO/1O1wZuR2oWjtcnGCJnQbvHLIokiEZ/NwoUeq0dv5oRH9wuWKAycu030zkrdTFZONJXTdoMsRmtBnaCASZc/RNh65ZxFV5SG3w5kxLUOXe15uamVFZRkPfK7C7VBmLBSIpxxd6+AMTegztO94O70Dw3m3q+JENKHnluNt1znWdp0dG2vzokhKOrrnvrM0oc9AfKGHxerFs7l/+Xy3w7HF2KCVtqByQmPYoizgY3ueFElJRxsMztKEPgMt1jVOd9xgx6a6vF3okUors+eOnptD/PSDS2y/bymzg/lRJCWdsWmL2mBwhCb0GXi5qZU5wWKeuHeJ26HYRltQuWPP0dEiKfk9VTGZXl/Oyiihi8hWEflQRM6JyPcmOW67iBgRqbcvxNzUeWOQt05e5qn6GkoDxW6HYxsdtMoNI9EYrx6yeGDlAlYuzK8iKZMJ6Tx0R6VN6CLiA34EPAqsAZ4RkTXjHDcb+F3gsN1B5qLdhy8Q9cBCj1Q6aJUb3j7TyaXewbzeF2g8t1eK6rRYJ2TSQt8AnDPGnDfGRIA9wOPjHPcfgP8MDNoYX06KjMTYfeQCD91VSV1Fmdvh2EqXZueGhiaL6rkhtuRhkZTJ6BiNszJJ6NVAW9LPFxPPjRGR9UCNMebvJvtFIvIdEWkWkeaurq4pB5srDpzsoKtvyBMLPVKN9XFql4trPursI3y+h+c31uLzwFTFZH6f4CsSvb4cMuNBUREpAv478AfpjjXGvGSMqTfG1FdWVs70o13TGLaoW1DKV1bl7//DRHTQyn0NTa2UFOdvkZTJiAjB4iK9vhySSUJvB5KvrKWJ50bNBtYC/ygircBGYJ9XB0ZPtvfSYl3jhU11nljokUoHrdzVOzDMG++3s23dEuaVBdwOxxG6o6dzMknoR4FVIrJcRALA08C+0ReNMb3GmApjTJ0xpg44BGwzxjQ7ErHLGppaKQ34eNIjCz1SlSSqyOs8YXfsbbnIwHDUk915o4K6575j0iZ0Y8wI8F3g58AZ4HVjzCkR+aGIbHM6wFxyrT/Cm8cv8Y17qykPeWOhRyoR0S10XRKLGXYdsrivdh5rq8vdDscxIb9PN+dySEYTqI0x+4H9Kc/9YIJjH5p5WLnp9kKPOrdDcZTeErvj3Y+7+LS7n997eJXboThKC5E7R1eKZigaM7xyyGLjivncvdg7Cz3GE/L7GND9qrOuMWxRObuER9fmd5GUdLRurXM0oWfo4JlO2q8P8KKH+zZHBf1FOk84y6yeft758ArPblhGoNjbX8t4l542GJzg7SvHRo1hi6ryIA97bKHHeEIBrcyebbvCFj6PFElJJ+gv0kFRh2hCz8C5K3388lw3z2+spdjn/VOmg6LZdSsywuvNbWxdu5hFc4Juh+M4vb6c4/3sZIPGsEXA582FHuPRPs7sevPYJW4Mjnh6qmIyHXR3jib0NPoGh9nbcpGvr6tiwawSt8PJivigqH7hsiFeJKWVNVVzqK+d53Y4WREvRK7XlxM0oaext+Ui/ZGo53a9m4z2oWfPkU+vcvZyHzs313qmSEo6IU3ojtGEPolYzNAYtvhSzVzW1cx1O5ys0T7O7GkMW5SH/GxbV53+YI8I+X0MRw3DUZ3pYjdN6JN475Nuznf3s3Ozt/Y8TyeoXS5Zcbl3kLdOXebpL9eM7aFTCHSLZudoQp9EQ1MrFbMCPPYFby/0SBXvctHWk9NePWwR82CRlHSCugGcYzShT6Dt6i0Onr3CMxuWUVJcOK0niN8SR6IxRvSW2DFDI1FeO3KBLasXUjO/1O1wsmqsyIWuRradJvQJvHLIoqhAFnqkGvvCjegXzikHTlym+2akYKYqJrt9fWkL3W6a0McxEImy52gbj9yziKrykNvhZN3YLbH2ozvm5aZWVlSW8cDnKtwOJeu0ELlzNKGPY9/xdnoHhgtqqmIyrfvorONt1znWdp0dG2s9WSQlHS1E7hxN6CniCz0sVi+ezYbl890OxxVahs5ZjWGLsoCP7R4tkpKOXl/O0YSeotm6xumOG+zYVFcwCz1S6S2xc3puDvHTDy6x/b6lzA56s0hKOmPTFvX6sp0m9BQNTa3MCRbzxL1L3A7FNXpL7JzbRVIKa6piMm2hO0cTepLOG4O8dfIyT9XXUBrIqJiTJ+kXzhkj0RivHrJ4cGUFKxd6u0jKZEYXUelaB/tpQk+y+/AFogW40CPV2BdOb4lt9faZTi71DhZ06xz0DtBJmtATIiMxdh+5wEN3VVJXUeZ2OK7SFrozGposqueG2FIARVImo7OonKMJPeHAyQ66+oYKcqFHKk3o9vuos4/w+R6e31iLrwCnKibz+wRfkeiguwM0oSc0hi3qFpTylVWVbofiOl1YZL/GcCslxYVTJGUyIkKwuEgbDA7QhA6cbO+lxbrGC5vqCnKhRyq9JbbXjcFh3ni/nW3rljCvLOB2ODlBqxY5QxM68amKpQEfTxboQo9Ufl8RxUWiXzib/KT5IrciUe3OSxL0+3TQ3QEFn9Cv9Ud48/glvnFvNeWhwlzoMZ54GTqdVjZTsZhh1yGL+2rnsba63O1wckbI79PNuRxQ8An99kKPOrdDySklWrXIFu9+3MWn3f0FP1UxVSigRVScUNAJPRozvHLIYtOKBdy9uHAXeownFCjSPnQbNIYtKmeX8OjawiqSkk5QGwyOKOiEfvBMJ+3XBwquxFwmQlqGbsasnn7e+fAKz25YRqC4oL9qd4jXrdUuPbsV9FXWEG5lSXmQhwt8ocd4tI9z5naFLXwFWiQlnaC/SAdFHVCwCf3clT7eO9fDcxtrKfYV7GmYkBaKnplbkRFeb25j69rFLJoTdDucnBPSLhdHZJTJRGSriHwoIudE5HvjvP77InJaRD4QkYMikvN9GI1hi4BPF3pMJF4oWr9w0/XmsUvcGBzhRZ2qOC69vpyRNqGLiA/4EfAosAZ4RkTWpBz2T0C9MeaLwE+A/2J3oHbqGxxmb8tFvr6uigWzStwOJydpC2r64kVSWllTNYf7aue5HU5O0kFRZ2TSQt8AnDPGnDfGRIA9wOPJBxhj3jHG3Er8eAjI6RU6e1su0h+JFmyJuUxoQp++I59e5ezlPnZuri3YIinphPzaQndCJgm9GmhL+vli4rmJfBs4MN4LIvIdEWkWkeaurq7Mo7RRLGZoDFt8qWYu62rmuhJDPggGdGHRdDWGLeaW+nn8S5N9TQpbyO9jOGoYjuo1ZidbRwNF5HmgHviT8V43xrxkjKk3xtRXVrqzCdZ7n3RzvrtfpyqmoS2o6bncO8hbpy7zrfqasX2/1Z2Cul+QIzJJ6O1A8sjh0sRznyEiDwPfB7YZY4bsCc9+DU2tVMwK8NgXdKHHZEa7XIwxboeSV149bBHTIilpje3oqQndVpkk9KPAKhFZLiIB4GlgX/IBInIv8H+IJ/Mr9odpj1kleNYAAA1BSURBVLartzh49grPbFhGSbG2niYTCviIxgzDUU3omRoaifLakQtsWb2QmvmlboeT08Z29NRuPVulTejGmBHgu8DPgTPA68aYUyLyQxHZljjsT4BZwF+LyDER2TfBr3PVK4csinShR0a0TNjUHThxme6bEd1VMQNjCV0Xr9kqo0rIxpj9wP6U536Q9Phhm+Oy3UAkyp6jbTxyzyKqykNuh5PzkvdE110oM/NyUysrKst44HMVboeS80KBeFtSF6/Zq2CWSO473k7vwLBOVcyQfuGm5njbdY61XWfHxlotkpIBvQN0RkEk9PhCD4vVi2ezYfl8t8PJC1pXdGoawxZlAR/btUhKRvT6ckZBJPRm6xqnO26wY1OdLvTIkLagMtdzc4iffnCJ7fctZXZQu6cyMTZtUe8AbVUQCb2hqZU5wWKeuHeJ26HkjZB+4TJ2u0iKTlXMlLbQneH5hN55Y5C3Tl7mqfoaSgMZjQEr4tMWQb9w6YxEY7x6yOLBlRWsXKhFUjI1en0N6p7otvJ8Qn/18AWiutBjyrQFlZm3z1zhUu+gts6nSLv0nOHphB4ZibH78AUeuquSuooyt8PJK2NfOO1ymVRDUyvVc0Ns0SIpUxLSpf+O8HRCP3Cyg+6bQ7rQYxpu3xLrF24iH3X2ET7fwwubavHpVMUp8fsEX5Fog8Fmnk7ojWGLugWlfGWVOxuB5TPtckmvMdxKSXER36rXIilTJSIEi4v0+rKZZxP6yfZeWqxrvLCpThd6TMPtLhcdtBrPjcFh3ni/nW3rljCvLOB2OHkpFNA99+3m2YTe0NRKacDHk7rQY1p8RUJAW1AT+knzRW5FotqdNwNBv0+nxdrMkwn9Wn+EN49f4hv3Vus+JDOge6KPLxYz7DpkcV/tPNZWl7sdTt4K+X26OZfNPJnQby/0qHM7lLwW8vt00Goc737cxafd/TpVcYZCAb2+7Oa5hB6NGV45ZLFpxQLuXqwLPWZC+zjH1xi2qJxdwqNrtUjKTGihaPt5LqEfPNNJ+/UBLTFnA/3C3cnq6eedD6/w7IZlBIo99/XJqnhVLB10t5PnrsiGcCtLyoM8rAs9ZizkL9I+9BS7whY+LZJii6C/SAdFbeaphH7uSh/vnevhuY21FPs89b/milBAB0WT3YqM8HpzG1vXLmbRnKDb4eS9kN4B2s5TWa8xbBHwFfH0l3Whhx30C/dZbx67xI3BEV7UqYq20AaD/TyT0PsGh9nbcpGvr6tiwawSt8PxhKDOchkTL5LSypqqOdxXO8/tcDxBx2js55mEvrflIv2RqLaebBT0+3R704Qjn17l7OU+dm6u1SIpNtF1DvbzREKPxQyNYYsv1czli0vnuh2OZ2iXy22NYYu5pX4e/1K126F4RsjvYzhqGI5qo8Eunkjo733Szfnufp2qaDNd+BF3uXeQt05d5lv1NWN73KiZC+oWurbzREJvaGqlYlaAx76gCz3sNNrHaYxxOxRXvXrYIqZFUmwX1KpYtsv7hN529RYHz17hmQ3LKCnW1pOdRrfQHRop3FvioZEorx25wJbVi6iZX+p2OJ4ydn3pOI1t8j6hv3LIokgXejgi5I9fHoXc7XLgxGW6b0a0O88Buue+/fI6oQ9Eouw52sYj9yyiqjzkdjieo4Wi4eWmVlZUlvHA5yrcDsVzQgFtMNgtrxP6vuPt9A4Ms1N3VXREoRfyPd52nWNt19mpRVIcUejXlxPyNqHHF3pYrF48mw3L57sdjieFCrxQdGPYoizg45vrdaqiE7TLxX55m9CbrWuc7rjBjk11utDDIYVcKLrn5hA//eAS2+9byuygFklxwti0xQJtMDghbxN6Q1Mrc4LFPHHvErdD8axCbkHdLpKig6FOKeTryyl5mdA7bwzy1snLPFVfQ2mg2O1wPCtYoF0uI9EYrx6yeHBlBSsXapEUp9y+A9Rpi3bJKKGLyFYR+VBEzonI98Z5vUREfpx4/bCI1NkdaLJXD18gagwvaOvJUYU6y+XtM1e41DuorXOH6aCo/dImdBHxAT8CHgXWAM+IyJqUw74NXDPGrAT+FPjPdgc6KjISY/fhC/zK3QupXVDm1Mcobt8SF1ofekNTK9VzQ2zRIimOKgv4KC4Sem4OuR2KZ2TSX7EBOGeMOQ8gInuAx4HTScc8Dvxx4vFPgD8XETEOrBk/cLKD7ptD2nrKgtGE/l///iP+8v996nI02RGJxrB6bvG9R1fj06mKjir2FbFy4Sx2hS1+cbrT7XCy6t9uWcWvr7N//C+ThF4NtCX9fBG4f6JjjDEjItILLAC6kw8Ske8A3wFYtmx6KzvLAsX82ppFfGVV5bTerzI3t9TPb/yz5bRfH3A7lKzasnqRbsOcJb/zq6v4uxOX3A4j68pDzsycyuqIojHmJeAlgPr6+mm13h9es4iH1+itcDaICN//WmrvmlL2+doXq/jaF3VTPbtkMijaDiTXdFuaeG7cY0SkGCgHeuwIUCmlVGYySehHgVUislxEAsDTwL6UY/YBOxOPnwT+wYn+c6WUUhNL2+WS6BP/LvBzwAf8lTHmlIj8EGg2xuwD/i+wS0TOAVeJJ32llFJZlFEfujFmP7A/5bkfJD0eBP65vaEppZSairxcKaqUUupOmtCVUsojNKErpZRHaEJXSimPELdmF4pIF2BN8+0VpKxCzREa19RoXFOXq7FpXFMzk7hqjTHjLpV3LaHPhIg0G2Pq3Y4jlcY1NRrX1OVqbBrX1DgVl3a5KKWUR2hCV0opj8jXhP6S2wFMQOOaGo1r6nI1No1rahyJKy/70JVSSt0pX1voSimlUmhCV0opj8i5hD6TgtQi8keJ5z8UkUeyHNfvi8hpEflARA6KSG3Sa1EROZb4k7r1sNNxvSgiXUmf/6+SXtspIh8n/uxMfa/Dcf1pUkwficj1pNecPF9/JSJXROTkBK+LiPxZIu4PRGR90muOnK8MYnouEcsJEWkSkXVJr7Umnj8mIs12xTSF2B4Skd6kv68fJL026TXgcFx/mBTTycQ1NT/xmiPnTERqROSdRB44JSK/O84xzl5fxpic+UN8e95PgBVAADgOrEk55reA/514/DTw48TjNYnjS4Dlid/jy2JcvwKUJh7/m9G4Ej/fdPF8vQj8+TjvnQ+cT/x3XuLxvGzFlXL87xDfltnR85X43V8B1gMnJ3j9MeAAIMBG4HAWzle6mDaPfhbxYu2Hk15rBSpcPF8PAT+b6TVgd1wpx/468RoNjp4zoApYn3g8G/honO+jo9dXrrXQxwpSG2MiwGhB6mSPAw2Jxz8BtoiIJJ7fY4wZMsZ8CpxL/L6sxGWMeccYcyvx4yHilZ2clsn5msgjwC+MMVeNMdeAXwBbXYrrGeA1mz57UsaYd4nv2T+Rx4FGE3cImCsiVTh4vtLFZIxpSnwmZO/aGv3sdOdrIjO5Nu2OKyvXlzGmwxjzfuJxH3CGeL3lZI5eX7mW0McrSJ16Qj5TkBoYLUidyXudjCvZt4n/KzwqKCLNInJIRJ6wKaapxLU9cXv3ExEZLSeYE+cr0TW1HPiHpKedOl+ZmCh2J8/XVKReWwb4exFpkXgRdjdsEpHjInJARO5JPJcT50tESoknxr1JTzt+ziTeFXwvcDjlJUevr6wWiS4EIvI8UA98NenpWmNMu4isAP5BRE4YYz7JUkg/BV4zxgyJyL8mfnfzq1n67Ew8DfzEGBNNes7N85WzRORXiCf0B5OefjBxrhYCvxCRs4nWa7a8T/zv66aIPAb8LbAqi5+fzq8D7xljklvzjp4zEZlF/B+Q3zPG3LDr92Yi11roMylIncl7nYwLEXkY+D6wzRgzNPq8MaY98d/zwD8S/5c7K3EZY3qSYvlL4L5M3+tkXEmeJuV22MHzlYmJYnfyfKUlIl8k/vf3uDFmrAB70rm6AvwN9nUzZsQYc8MYczPxeD/gF5EKXD5fSSa7vmw/ZyLiJ57MXzXGvDHOIc5eX3YPDMxwUKGY+GDAcm4PpNyTcsxv89lB0dcTj+/hs4Oi57FvUDSTuO4lPgi0KuX5eUBJ4nEF8DE2DQ5lGFdV0uNvAIfM7UGYTxPxzUs8np+tuBLHrSY+QCXZOF9Jn1HHxIN8X+Ozg1ZHnD5fGcS0jPiY0OaU58uA2UmPm4Ctdp6rDGJbPPr3RzwxXkicu4yuAafiSrxeTryfvSwb5yzx/90I/I9JjnH0+rL1L96mk/IY8dHhT4DvJ577IfFWL0AQ+OvEBX4EWJH03u8n3vch8GiW43ob6ASOJf7sSzy/GTiRuKBPAN/Oclz/ETiV+Px3gNVJ7/2XifN4DvgX2Ywr8fMfA/8p5X1On6/XgA5gmHg/5beB3wR+M/G6AD9KxH0CqHf6fGUQ018C15KurebE8ysS5+l44u/4+3aeqwxj+27S9XWIpH90xrsGshVX4pgXiU+USH6fY+eMeFeYAT5I+rt6LJvXly79V0opj8i1PnSllFLTpAldKaU8QhO6Ukp5hCZ0pZTyCE3oSinlEZrQlVLKIzShK6WUR/x/Qb584xfmSVkAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "x = np.linspace(0,2,1000)\n",
    "\n",
    "triangle_ufunc = np.frompyfunc(triangle_wave, 4, 1)\n",
    "y = triangle_ufunc(x, 0.6, 0.55, 1.0).astype(np.float)  # 将 frompyfunc 返回类型 object 转换成 float\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "plt.plot(x, y)\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## 广播\n",
    "如果形状不同，会进行如下广播（broadcasting）  \n",
    "1、让所有输入数组都向其中维数最多的数组看齐，shape属性中不足的部分都通过在前面 加1 补齐  \n",
    "2、输出数组的shape属性是输入数组的shape属性在各个轴上的最大值  \n",
    "3、如果输入数组的某个轴长度为1或与输出数组对应轴的长度相同，这个数组就能够用来计算，否则出错  \n",
    "4、当输入数组的某个轴长度为1时，沿着此轴运算时都用此轴上的第一组值  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "a Shape: (5, 2)\n",
      "[[0 1]\n",
      " [2 3]\n",
      " [4 5]\n",
      " [6 7]\n",
      " [8 9]]\n",
      "\n",
      "b Shape: (2,)\n",
      "[ 10 100]\n",
      "\n",
      "a + b Shape: (5, 2)\n",
      "[[ 10 101]\n",
      " [ 12 103]\n",
      " [ 14 105]\n",
      " [ 16 107]\n",
      " [ 18 109]]\n",
      "\n",
      "np.add.outer(a, b) Shape: [[[ 10 100]\n",
      "  [ 11 101]]\n",
      "\n",
      " [[ 12 102]\n",
      "  [ 13 103]]\n",
      "\n",
      " [[ 14 104]\n",
      "  [ 15 105]]\n",
      "\n",
      " [[ 16 106]\n",
      "  [ 17 107]]\n",
      "\n",
      " [[ 18 108]\n",
      "  [ 19 109]]]\n",
      "[[[ 10 100]\n",
      "  [ 11 101]]\n",
      "\n",
      " [[ 12 102]\n",
      "  [ 13 103]]\n",
      "\n",
      " [[ 14 104]\n",
      "  [ 15 105]]\n",
      "\n",
      " [[ 16 106]\n",
      "  [ 17 107]]\n",
      "\n",
      " [[ 18 108]\n",
      "  [ 19 109]]]\n",
      "\n",
      "np.add.reduce(a, axis=1) Shape: (5,)\n",
      "[ 1  5  9 13 17]\n"
     ]
    }
   ],
   "source": [
    "# a = np.arange(5).reshape(-1, 1)\n",
    "a = np.arange(10).reshape(-1, 2)\n",
    "b = np.array([10, 100])\n",
    "\n",
    "print('\\n{} Shape: {}\\n{}'.format('a', a.shape, a))\n",
    "print('\\n{} Shape: {}\\n{}'.format('b', b.shape, b))\n",
    "\n",
    "print('\\n{} Shape: {}\\n{}'.format('a + b', (a + b).shape, a + b))\n",
    "\n",
    "print('\\n{} Shape: {}\\n{}'.format('np.add.outer(a, b)', np.add.outer(a, b), np.add.outer(a, b)))\n",
    "print('\\n{} Shape: {}\\n{}'.format('np.add.reduce(a, axis=1)', np.add.reduce(a, axis=1).shape, np.add.reduce(a, axis=1)))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "x Shape: (5, 1)\n",
      "[[0]\n",
      " [1]\n",
      " [2]\n",
      " [3]\n",
      " [4]]\n",
      "\n",
      "y Shape: (1, 5)\n",
      "[[0 1 2 3 4]]\n",
      "\n",
      "x + y Shape: (5, 5)\n",
      "[[0 1 2 3 4]\n",
      " [1 2 3 4 5]\n",
      " [2 3 4 5 6]\n",
      " [3 4 5 6 7]\n",
      " [4 5 6 7 8]]\n",
      "\n",
      "\n",
      "x Shape: (5, 5)\n",
      "[[0 1 2 3 4]\n",
      " [0 1 2 3 4]\n",
      " [0 1 2 3 4]\n",
      " [0 1 2 3 4]\n",
      " [0 1 2 3 4]]\n",
      "\n",
      "y Shape: (5, 5)\n",
      "[[0 0 0 0 0]\n",
      " [1 1 1 1 1]\n",
      " [2 2 2 2 2]\n",
      " [3 3 3 3 3]\n",
      " [4 4 4 4 4]]\n"
     ]
    }
   ],
   "source": [
    "x, y = np.ogrid[:5,:5]\n",
    "print('\\n{} Shape: {}\\n{}'.format('x', x.shape, x))\n",
    "print('\\n{} Shape: {}\\n{}'.format('y', y.shape, y))\n",
    "print('\\n{} Shape: {}\\n{}\\n'.format('x + y', (x + y).shape, x + y))\n",
    "\n",
    "x, y = np.meshgrid(np.arange(5), np.arange(5))\n",
    "print('\\n{} Shape: {}\\n{}'.format('x', x.shape, x))\n",
    "print('\\n{} Shape: {}\\n{}'.format('y', y.shape, y))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## 随机数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "np.random.rand(4,5) Shape: (4, 5)\n",
      "[[0.18 0.81 0.51 0.76 0.4 ]\n",
      " [0.82 0.91 0.88 0.8  0.87]\n",
      " [0.92 0.67 0.25 0.76 0.21]\n",
      " [0.28 0.69 0.55 0.18 0.94]]\n",
      "\n",
      "np.random.randn(4,5) Shape: (4, 5)\n",
      "[[ 1.17 -0.68 -0.38 -1.27  1.24]\n",
      " [-0.55  1.78  0.79 -1.78 -0.1 ]\n",
      " [-0.7   1.27 -1.58  1.81 -1.31]\n",
      " [ 0.77  0.8   0.15  0.05 -0.67]]\n",
      "\n",
      "np.random.randint(0, 10, size=(4,5)) Shape: (4, 5)\n",
      "[[1 6 3 3 6]\n",
      " [6 2 2 7 6]\n",
      " [0 2 2 8 1]\n",
      " [3 5 3 4 5]]\n",
      "\n",
      "np.random.uniform(0, 10, size=(4,5)) Shape: (4, 5)\n",
      "[[5.21 3.15 4.84 6.12 9.56]\n",
      " [9.52 7.15 8.82 2.13 6.59]\n",
      " [7.32 9.58 9.03 2.97 0.12]\n",
      " [7.39 9.39 3.28 5.28 0.25]]\n",
      "\n",
      "np.random.permutation\n",
      "打乱前：[0 1 2 3 4 5 6 7 8 9] \n",
      "返回：[3 9 1 5 8 6 2 0 7 4]\n",
      "\n",
      "np.random.shuffle\n",
      "打乱前：[4 5 7 8 1 9 2 0 6 3] \n",
      "返回：None\n",
      "\n",
      "np.random.choice(a, size=(3, 5), replace=True, p=np.arange(10)/a.sum())：\n",
      "[[0 6 6 6 3]\n",
      " [9 3 6 6 2]\n",
      " [2 3 2 0 9]]\n"
     ]
    }
   ],
   "source": [
    "np.set_printoptions(precision=2)  # 只显示小数点后两位\n",
    "np.random.seed(None)  # 设置随机种子 \n",
    "print('\\n{} Shape: {}\\n{}'.format('np.random.rand(4,5)', np.random.rand(4,5).shape, np.random.rand(4,5)))  # 0 到 1 之间的随机数\n",
    "print('\\n{} Shape: {}\\n{}'.format('np.random.randn(4,5)', np.random.randn(4,5).shape, np.random.randn(4,5)))  # 标准正态分布随机数\n",
    "print('\\n{} Shape: {}\\n{}'.format('np.random.randint(0, 10, size=(4,5))', np.random.randint(0, 10, size=(4,5)).shape, np.random.randint(0, 10, size=(4,5))))  # 指定范围的随机整数\n",
    "print('\\n{} Shape: {}\\n{}'.format('np.random.uniform(0, 10, size=(4,5))', np.random.uniform(0, 10, size=(4,5)).shape, np.random.uniform(0, 10, size=(4,5))))  # 指定范围的均匀分布随机数\n",
    "\n",
    "a = np.arange(10)\n",
    "\n",
    "# 随机打乱顺序（permutation: 返回打乱后数组，原数组不做改变；  shuffle 返回None，在原数组上改变）\n",
    "print('\\nnp.random.permutation\\n打乱前：{} \\n返回：{}'.format(a, np.random.permutation(a)))\n",
    "print('\\nnp.random.shuffle\\n打乱前：{} \\n返回：{}'.format(a, np.random.shuffle(a)))\n",
    "print('\\nnp.random.choice(a, size=(3, 5), replace=True, p=np.arange(10)/a.sum())：\\n{}'.format(\n",
    "    np.random.choice(a, size=(3, 5), replace=True, p=np.arange(10)/a.sum())))  # 随机抽样 \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## 统计"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Raw a Shape: (4, 5)\n",
      "[[5 0 3 3 7]\n",
      " [9 3 5 2 4]\n",
      " [7 6 8 8 1]\n",
      " [6 7 7 8 1]]\n",
      "\n",
      "a.sum(axis=0) Shape: (5,)\n",
      "[27 16 23 21 13]\n",
      "\n",
      "a.max(axis=0) Shape: (5,)\n",
      "[9 7 8 8 7]\n",
      "\n",
      "a.argmax(axis=0) Shape: (5,)\n",
      "[1 3 2 2 0]\n",
      "\n",
      "a.min(axis=0) Shape: (5,)\n",
      "[5 0 3 2 1]\n",
      "\n",
      "a.argmin(axis=0) Shape: (5,)\n",
      "[0 0 0 1 2]\n",
      "\n",
      "a.ptp(axis=0) Shape: (5,)\n",
      "[4 7 5 6 6]\n",
      "\n",
      "a.mean(axis=0) Shape: (5,)\n",
      "[6.75 4.   5.75 5.25 3.25]\n",
      "\n",
      "np.average(a,weights=[1,1,1,10],axis=0) Shape: (5,)\n",
      "[6.23 6.08 6.62 7.15 1.69]\n",
      "\n",
      "np.median(a, axis=1) Shape: (4,)\n",
      "[3. 4. 7. 7.]\n",
      "\n",
      "np.std(a, axis=1) Shape: (4,)\n",
      "[2.33 2.42 2.61 2.48]\n",
      "\n",
      "np.var(a, axis=1) Shape: (4,)\n",
      "[5.44 5.84 6.8  6.16]\n",
      "\n",
      "np.product(a, axis=0) Shape: (5,)\n",
      "[1890    0  840  384   28]\n",
      "\n",
      "np.percentile(a, [0.1, 0.5, 0.8], axis=1) Shape: (3, 4)\n",
      "[[0.01 2.   1.02 1.02]\n",
      " [0.06 2.02 1.1  1.1 ]\n",
      " [0.1  2.03 1.16 1.16]]\n",
      "\n",
      "Raw a Shape: (4, 5, 2)\n",
      "aa.sum(axis=(0,2)) Shape: (5,)\n",
      "[ 55  61 119  90  48]\n"
     ]
    }
   ],
   "source": [
    "np.random.seed(0)\n",
    "a = np.random.randint(0, 10, size=(4,5))\n",
    "print('\\n{} Shape: {}\\n{}'.format('Raw a', a.shape, a))\n",
    "# np.sum, mean, ptp\n",
    "print('\\n{} Shape: {}\\n{}'.format('a.sum(axis=0)', a.sum(axis=0).shape, a.sum(axis=0)))\n",
    "print('\\n{} Shape: {}\\n{}'.format('a.max(axis=0)', a.max(axis=0).shape, a.max(axis=0)))\n",
    "print('\\n{} Shape: {}\\n{}'.format('a.argmax(axis=0)', a.argmax(axis=0).shape, a.argmax(axis=0)))  # 最大值索引 \n",
    "print('\\n{} Shape: {}\\n{}'.format('a.min(axis=0)', a.min(axis=0).shape, a.min(axis=0)))\n",
    "print('\\n{} Shape: {}\\n{}'.format('a.argmin(axis=0)', a.argmin(axis=0).shape, a.argmin(axis=0)))  # 最小值索引 \n",
    "print('\\n{} Shape: {}\\n{}'.format('a.ptp(axis=0)', a.ptp(axis=0).shape, a.ptp(axis=0)))  # 极值\n",
    "print('\\n{} Shape: {}\\n{}'.format('a.mean(axis=0)', a.mean(axis=0).shape, a.mean(axis=0)))  # 均值 \n",
    "print('\\n{} Shape: {}\\n{}'.format('np.average(a,weights=[1,1,1,10],axis=0)', \n",
    "                                  np.average(a,weights=[1,1,1,10], axis=0).shape, np.average(a,weights=[1,1,1,10],axis=0)))  # 加权 均值 \n",
    "print('\\n{} Shape: {}\\n{}'.format('np.median(a, axis=1)', np.median(a, axis=1).shape, np.median(a, axis=1)))  # 中位数 \n",
    "print('\\n{} Shape: {}\\n{}'.format('np.std(a, axis=1)', np.std(a, axis=1).shape, np.std(a, axis=1)))  # 标准差 \n",
    "print('\\n{} Shape: {}\\n{}'.format('np.var(a, axis=1)', np.var(a, axis=1).shape, np.var(a, axis=1)))  # 方差 \n",
    "print('\\n{} Shape: {}\\n{}'.format('np.product(a, axis=0)', np.product(a, axis=0).shape, np.product(a, axis=0)))  # 连乘积 \n",
    "print('\\n{} Shape: {}\\n{}'.format('np.percentile(a, [0.1, 0.5, 0.8], axis=1)', \n",
    "                                  np.percentile(a, [0.1, 0.5, 0.8], axis=1).shape, np.percentile(a, [0.1, 0.5, 0.8], axis=1)))  # 计算百分位数 \n",
    "# 多维 axis \n",
    "aa = np.random.randint(0, 20, size=(4,5,2))\n",
    "print('\\n{} Shape: {}'.format('Raw a', aa.shape))\n",
    "print('{} Shape: {}\\n{}'.format('aa.sum(axis=(0,2))', aa.sum(axis=(0,2)).shape, aa.sum(axis=(0,2))))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "区间统计结果： [ 4  4 12]\n",
      "区间范围： [ 0  3  5 10]\n",
      "\n",
      "\n",
      "x_unique 数组中出现的元素：\n",
      "[0 1 2 3 4 5 6 7 8 9]\n",
      "\n",
      "x_unique 数组中出现的元素在数组中的坐标：\n",
      "[ 1 14  8  2  9  0 11  4 12  5]\n",
      "\n",
      "数组各个元素在x_unique的坐标：\n",
      "[5 0 3 3 7 9 3 5 2 4 7 6 8 8 1 6 7 7 8 1]\n",
      "\n",
      "\n",
      "x_unique 数组各元素出现的次数：\n",
      "[1 2 1 3 1 2 2 4 3 1]\n",
      "\n",
      "x_unique 数组各元素出现的次数加权和：\n",
      "[2. 4. 2. 6. 2. 4. 4. 8. 6. 2.]\n",
      "\n",
      "\n",
      "digitize离散化前：\n",
      "[0.  0.5 1.  1.5 2.  2.5 3.  3.5 4.  4.5 5.  5.5 6.  6.5 7.  7.5 8.  8.5\n",
      " 9.  9.5]\n",
      "离散化前：\n",
      "[1 1 2 2 2 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4]\n"
     ]
    }
   ],
   "source": [
    "# hist 区间统计结果，  bin_edges 区间范围长度为（len(hist)+1）\n",
    "hist, bin_edges = np.histogram(a, bins=10, range=(0,10))   # bins 区间数量， range 区间范围默认为 (min,max)\n",
    "hist, bin_edges = np.histogram(a, bins=[0,3,5,10], range=(0,10))   # bins 区间数量， range 区间范围默认为 (min,max)\n",
    "print('区间统计结果：', hist)\n",
    "print('区间范围：', bin_edges)\n",
    "\n",
    "# unique 去除重复元素 查找数组中的所有整数，并按照顺序排列\n",
    "x_unique, x_index, x_index_all = np.unique(a, return_index=True, return_inverse=True)\n",
    "print('\\n\\nx_unique 数组中出现的元素：\\n{}'.format(x_unique))\n",
    "print('\\nx_unique 数组中出现的元素在数组中的坐标：\\n{}'.format(x_index))\n",
    "print('\\n数组各个元素在x_unique的坐标：\\n{}'.format(x_index_all))\n",
    "\n",
    "# bincount 对整数数组的元素计数\n",
    "print('\\n\\nx_unique 数组各元素出现的次数：\\n{}'.format(np.bincount(a.ravel())))\n",
    "print('\\nx_unique 数组各元素出现的次数加权和：\\n{}'.format(np.bincount(a.ravel(), 2*np.ones_like(a.ravel()))))\n",
    "\n",
    "# digitize  离散化\n",
    "x = np.arange(0, 10, 0.5)\n",
    "bins = np.array([0.0, 1.0, 2.5, 4.0, 10.0])\n",
    "inds = np.digitize(x, bins)\n",
    "print('\\n\\ndigitize离散化前：\\n{}\\n离散化前：\\n{}'.format(x, inds))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## 排序"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "最大值位置：(1, 0)   Max:9\n",
      "\n",
      "升序：\n",
      " [[0 3 3 5 7]\n",
      " [2 3 4 5 9]\n",
      " [1 6 7 8 8]\n",
      " [1 6 7 7 8]]\n",
      "\n",
      "降序：\n",
      " [[7 5 3 3 0]\n",
      " [9 5 4 3 2]\n",
      " [8 8 7 6 1]\n",
      " [8 7 7 6 1]]\n",
      "\n",
      "排序下标 axis=1：\n",
      " [[1 2 3 0 4]\n",
      " [3 1 4 2 0]\n",
      " [4 1 0 2 3]\n",
      " [4 0 1 2 3]]\n",
      "\n",
      "排序下标 整个数组：\n",
      " (array([0, 3, 2, 1, 0, 0, 1, 1, 1, 0, 2, 3, 2, 0, 3, 3, 3, 2, 2, 1]), array([1, 4, 4, 3, 2, 3, 1, 4, 2, 0, 1, 0, 0, 4, 1, 2, 3, 2, 3, 0]))\n",
      "\n",
      "np.partition(a, kth=2, axis=1) 快速计算前 kth 位。\n",
      "[[0 3]\n",
      " [2 3]\n",
      " [1 6]\n",
      " [1 6]]\n",
      "\n",
      "np.argpartition(a, kth=2, axis=1) 快速计算前 kth 位坐标。 Shape: (4, 5)\n",
      "[[1 2]\n",
      " [3 1]\n",
      " [4 1]\n",
      " [4 0]]\n",
      "\n",
      "\n",
      "a:  [1, 5, 1, 4, 3, 4, 4]\n",
      "\n",
      "b:  [9, 4, 0, 4, 0, 2, 1]\n",
      "\n",
      "多列排序：\n",
      " [(1, 0), (1, 9), (3, 0), (4, 1), (4, 2), (4, 4), (5, 4)]\n"
     ]
    }
   ],
   "source": [
    "# 最大值位置\n",
    "max_index = a.argmax()\n",
    "# 将一维坐标 转换成 多维中的坐标\n",
    "max_loc = np.unravel_index(max_index, a.shape)\n",
    "print('最大值位置：{}   Max:{}'.format(max_loc, a[max_loc]))\n",
    "\n",
    "# ### 排序 \n",
    "a_ascending = np.sort(a, axis=1)    # 升序\n",
    "a_descending = -np.sort(-a, axis=1) # 降序\n",
    "\n",
    "print('\\n升序：\\n', a_ascending)\n",
    "print('\\n降序：\\n', a_descending)\n",
    "\n",
    "print('\\n排序下标 axis=1：\\n', np.argsort(a, axis=1))\n",
    "print('\\n排序下标 整个数组：\\n', np.unravel_index(np.argsort(a.ravel()), a.shape))\n",
    "\n",
    "# 快速计算前 kth 位，默认升序 (注意，返回前TopK个数值没有进行排序)\n",
    "print('\\n{} 快速计算前 kth 位。\\n{}'.format('np.partition(a, kth=2, axis=1)', np.partition(a, kth=2, axis=1)[:,:2]))\n",
    "# 快速计算前 kth 位坐标，默认升序\n",
    "print('\\n{} 快速计算前 kth 位坐标。 Shape: {}\\n{}'.format('np.argpartition(a, kth=2, axis=1)', \n",
    "                                  np.argpartition(a, kth=2, axis=1).shape, np.argpartition(a, kth=2, axis=1)[:,:2]))\n",
    "\n",
    "# 多列排序 \n",
    "l_a = [1,5,1,4,3,4,4] # First column\n",
    "l_b = [9,4,0,4,0,2,1] # Second column\n",
    "ind = np.lexsort((l_b,l_a)) # Sort by a, then by b\n",
    "print('\\n\\na: ', l_a)\n",
    "print('\\nb: ', l_b)\n",
    "print('\\n多列排序：\\n', [(l_a[i],l_b[i]) for i in ind])\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## 分段函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "np.where(a > 5) 匹配条件位置坐标:\n",
      "(array([0, 1, 2, 2, 2, 2, 3, 3, 3, 3]), array([4, 0, 0, 1, 2, 3, 0, 1, 2, 3]))\n",
      "\n",
      "a[np.where(a > 5)] 匹配条件元素值:\n",
      "[7 9 7 6 8 8 6 7 7 8]\n",
      "\n",
      "np.where(a > 5, a, 0) 匹配条件元素值替换:\n",
      "[[0 0 0 0 7]\n",
      " [9 0 0 0 0]\n",
      " [7 6 8 8 0]\n",
      " [6 7 7 8 0]]\n",
      "\n",
      "\n",
      "原始数据：\n",
      "[[5 0 3 3 7]\n",
      " [9 3 5 2 4]\n",
      " [7 6 8 8 1]\n",
      " [6 7 7 8 1]]\n",
      "\n",
      "多条件处理 np.select([a>5, a<2, (2<a)&(a<5)], [a*10, a+10000, 9999])：\n",
      "[[-11111  10000   9999   9999     70]\n",
      " [    90   9999 -11111 -11111   9999]\n",
      " [    70     60     80     80  10001]\n",
      " [    60     70     70     80  10001]]\n",
      "\n",
      "\n",
      "np.choose：\n",
      "[[999  22   3]\n",
      " [100 999   6]\n",
      " [  7 100 999]]\n",
      "\n",
      "\n",
      "np.piecewise：\n",
      "[[-11111  10000   9999   9999     70]\n",
      " [    90   9999 -11111 -11111   9999]\n",
      " [    70     60     80     80  10001]\n",
      " [    60     70     70     80  10001]]\n"
     ]
    }
   ],
   "source": [
    "### np.where\n",
    "condition_index = np.where(a > 5)\n",
    "print('{} 匹配条件位置坐标:\\n{}'.format('np.where(a > 5)', np.where(a > 5)))  # 匹配条件位置坐标\n",
    "print('\\n{} 匹配条件元素值:\\n{}'.format('a[np.where(a > 5)]', a[np.where(a > 5)]))   # 匹配条件元素值\n",
    "print('\\n{} 匹配条件元素值替换:\\n{}'.format('np.where(a > 5, a, 0)', np.where(a > 5, a, 0)))  \n",
    "\n",
    "## select(condlist, choicelist, default=0)  对于多条件、多操作的筛选和执行 \n",
    "print('\\n\\n原始数据：\\n{}'.format(a))\n",
    "print('\\n多条件处理 {}：\\n{}'.format('np.select([a>5, a<2, (2<a)&(a<5)], [a*10, a+10000, 9999])',\n",
    "                           np.select([a>5, a<2, (2<a)&(a<5)], [a*10, a+10000, 9999], default=-11111)))\n",
    "\n",
    "## np.choose\n",
    "c1=[[1,2,3],[4,5,6],[7,8,9]]\n",
    "c2=[[11,22,33],[44,55,66],[77,88,99]]\n",
    "c3=[[999,999,999],[999,999,999],[999,999,999]]\n",
    "\n",
    "choices=[c1,c2,c3,100]\n",
    "print('\\n\\nnp.choose：\\n{}'.format(np.choose([[2,1,0],[3,2,0],[0,3,2]],choices)))\n",
    "\n",
    "## np.piecewise 分段函数  效率高，计算只需要计算的值\n",
    "print('\\n\\nnp.piecewise：\\n{}'.format(np.piecewise(a, [a>5, a<2, (2<a)&(a<5)], [lambda x:x*10, lambda x:x+10000, 9999, -11111])))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## 矩阵计算"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "二维数组：\n",
      "\n",
      "a Shape:(2, 5) \n",
      "[[0 1 2 3 4]\n",
      " [5 6 7 8 9]]\n",
      "\n",
      "b Shape:(5, 3) \n",
      "[[0 0 0]\n",
      " [1 1 1]\n",
      " [2 2 2]\n",
      " [3 3 3]\n",
      " [4 4 4]]\n",
      "\n",
      "二维数组内积 a.dot(b)  Shape:(2, 3) \n",
      "[[30 30 30]\n",
      " [80 80 80]]\n",
      "\n",
      "\n",
      "三维数组：\n",
      "\n",
      "a Shape:(2, 3, 4) \n",
      "[[[ 0  1  2  3]\n",
      "  [ 4  5  6  7]\n",
      "  [ 8  9 10 11]]\n",
      "\n",
      " [[12 13 14 15]\n",
      "  [16 17 18 19]\n",
      "  [20 21 22 23]]]\n",
      "\n",
      "b Shape:(1, 4, 3) \n",
      "[[[0 0 0]\n",
      "  [1 1 1]\n",
      "  [2 2 2]\n",
      "  [3 3 3]]]\n",
      "\n",
      "三维数组内积 np.dot(a,b)  Shape:(2, 3, 1, 3) \n",
      "[[[[ 14  14  14]]\n",
      "\n",
      "  [[ 38  38  38]]\n",
      "\n",
      "  [[ 62  62  62]]]\n",
      "\n",
      "\n",
      " [[[ 86  86  86]]\n",
      "\n",
      "  [[110 110 110]]\n",
      "\n",
      "  [[134 134 134]]]]\n"
     ]
    }
   ],
   "source": [
    "###############################################\n",
    "# 二维数组 矩阵内积\n",
    "print('二维数组：')\n",
    "a =  np.arange(10).reshape(2,5)\n",
    "b =  np.repeat(np.arange(5), 3).reshape(5,3)\n",
    "print('\\n{} Shape:{} \\n{}'.format('a', a.shape, a))\n",
    "print('\\n{} Shape:{} \\n{}'.format('b', b.shape, b))\n",
    "print('\\n{} Shape:{} \\n{}'.format('二维数组内积 a.dot(b) ', (a.dot(b)).shape, a.dot(b)))\n",
    "\n",
    "# 三维数组 矩阵内积  dot(a,b)[i,j,k,m] = sum(a[i,j,:]*b[k,:,m])\n",
    "print('\\n\\n三维数组：')\n",
    "a = np.arange(24).reshape(2,3,4)\n",
    "b = np.repeat(np.arange(4), 3).reshape(1,4,3)\n",
    "print('\\n{} Shape:{} \\n{}'.format('a', a.shape, a))\n",
    "print('\\n{} Shape:{} \\n{}'.format('b', b.shape, b))\n",
    "print('\\n{} Shape:{} \\n{}'.format('三维数组内积 np.dot(a,b) ', np.dot(a,b).shape, np.dot(a,b)))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "二维数组：\n",
      "\n",
      "a Shape:(2, 5) \n",
      "[[0 1 2 3 4]\n",
      " [5 6 7 8 9]]\n",
      "\n",
      "b Shape:(5, 3) \n",
      "[[0 0 0]\n",
      " [1 1 1]\n",
      " [2 2 2]\n",
      " [3 3 3]\n",
      " [4 4 4]]\n",
      "\n",
      "np.tensordot(a,b, axes=[[1],[0]])  \n",
      "[[30 30 30]\n",
      " [80 80 80]]\n",
      "\n",
      "np.tensordot(a,b, axes=1)  \n",
      "[[30 30 30]\n",
      " [80 80 80]]\n",
      "\n",
      "\n",
      "三维数组：\n",
      "\n",
      "a Shape:(2, 3, 4) \n",
      "[[[ 0  1  2  3]\n",
      "  [ 4  5  6  7]\n",
      "  [ 8  9 10 11]]\n",
      "\n",
      " [[12 13 14 15]\n",
      "  [16 17 18 19]\n",
      "  [20 21 22 23]]]\n",
      "\n",
      "b Shape:(4, 3, 1) \n",
      "[[[0]\n",
      "  [0]\n",
      "  [0]]\n",
      "\n",
      " [[1]\n",
      "  [1]\n",
      "  [1]]\n",
      "\n",
      " [[2]\n",
      "  [2]\n",
      "  [2]]\n",
      "\n",
      " [[3]\n",
      "  [3]\n",
      "  [3]]]\n",
      "\n",
      "np.tensordot(a,b, [(1,2),(1,0)]) Shape:(2, 1) \n",
      "[[114]\n",
      " [330]]\n"
     ]
    }
   ],
   "source": [
    "###############################################\n",
    "# 二维数组 矩阵内积  tensordot()  将两个多维数组 a 和 b 指定轴上的相应元素相乘并求和\n",
    "print('二维数组：')\n",
    "a =  np.arange(10).reshape(2,5)\n",
    "b =  np.repeat(np.arange(5), 3).reshape(5,3)\n",
    "print('\\n{} Shape:{} \\n{}'.format('a', a.shape, a))\n",
    "print('\\n{} Shape:{} \\n{}'.format('b', b.shape, b))\n",
    "print('\\n{} \\n{}'.format('np.tensordot(a,b, axes=[[1],[0]]) ', np.tensordot(a,b, axes=[[1],[0]])))\n",
    "print('\\n{} \\n{}'.format('np.tensordot(a,b, axes=1) ', np.tensordot(a,b, axes=1)))\n",
    "\n",
    "# 三维数组 矩阵内积  c(a,b)[i,j,k,m] = sum(a[i,:,:,j]*b[:,:,k,m].T)\n",
    "print('\\n\\n三维数组：')\n",
    "# a = np.random.rand(4,5,6,7)\n",
    "# b = np.random.rand(6,5,2,3)\n",
    "# a = np.random.randint(0,9,(3,4,5))\n",
    "# b = np.random.randint(0,9,(5,4,2))\n",
    "\n",
    "a = np.arange(24).reshape(2,3,4)\n",
    "b = np.repeat(np.arange(4), 3).reshape(4,3,1)\n",
    "\n",
    "print('\\n{} Shape:{} \\n{}'.format('a', a.shape, a))\n",
    "print('\\n{} Shape:{} \\n{}'.format('b', b.shape, b))\n",
    "c = np.tensordot(a,b, [(1,2),(1,0)])\n",
    "print('\\n{} Shape:{} \\n{}'.format('np.tensordot(a,b, [(1,2),(1,0)])', c.shape, c))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "二维数组：\n",
      "\n",
      "a Shape:(2, 5) \n",
      "[[0 1 2 3 4]\n",
      " [5 6 7 8 9]]\n",
      "\n",
      "b Shape:(3, 5) \n",
      "[[0 0 0 0 0]\n",
      " [1 1 1 1 1]\n",
      " [2 2 2 2 2]]\n",
      "\n",
      "二维数组内积 np.inner(a, b)  Shape:(2, 3) \n",
      "[[ 0 10 20]\n",
      " [ 0 35 70]]\n",
      "\n",
      "\n",
      "三维数组：\n",
      "\n",
      "a Shape:(2, 3, 4) \n",
      "[[[ 0  1  2  3]\n",
      "  [ 4  5  6  7]\n",
      "  [ 8  9 10 11]]\n",
      "\n",
      " [[12 13 14 15]\n",
      "  [16 17 18 19]\n",
      "  [20 21 22 23]]]\n",
      "\n",
      "b Shape:(1, 5, 4) \n",
      "[[[0 0 0 0]\n",
      "  [1 1 1 1]\n",
      "  [2 2 2 2]\n",
      "  [3 3 3 3]\n",
      "  [4 4 4 4]]]\n",
      "\n",
      "三维数组内积 np.inner(a, b)  Shape:(2, 3, 1, 5) \n",
      "[[[[  0   6  12  18  24]]\n",
      "\n",
      "  [[  0  22  44  66  88]]\n",
      "\n",
      "  [[  0  38  76 114 152]]]\n",
      "\n",
      "\n",
      " [[[  0  54 108 162 216]]\n",
      "\n",
      "  [[  0  70 140 210 280]]\n",
      "\n",
      "  [[  0  86 172 258 344]]]]\n"
     ]
    }
   ],
   "source": [
    "###############################################\n",
    "# inner 内积  各 dot 乘积一样，\n",
    "# 二维数组 inner\n",
    "print('二维数组：')\n",
    "a =  np.arange(10).reshape(2,5)\n",
    "b =  np.repeat(np.arange(3), 5).reshape(3,5)\n",
    "print('\\n{} Shape:{} \\n{}'.format('a', a.shape, a))\n",
    "print('\\n{} Shape:{} \\n{}'.format('b', b.shape, b))\n",
    "print('\\n{} Shape:{} \\n{}'.format('二维数组内积 np.inner(a, b) ', (np.inner(a, b)).shape, np.inner(a, b)))\n",
    "\n",
    "# 三维数组 inner(a,b)[i,j,k,m] = sum(a[i,j,:]*b[k,m,:])\n",
    "print('\\n\\n三维数组：')\n",
    "a = np.arange(24).reshape(2,3,4)\n",
    "b = np.repeat(np.arange(5), 4).reshape(1,5,4)\n",
    "print('\\n{} Shape:{} \\n{}'.format('a', a.shape, a))\n",
    "print('\\n{} Shape:{} \\n{}'.format('b', b.shape, b))\n",
    "print('\\n{} Shape:{} \\n{}'.format('三维数组内积 np.inner(a, b) ', np.inner(a, b).shape, np.inner(a, b)))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "a Shape:(3,) \n",
      "[0 1 2]\n",
      "\n",
      "b Shape:(5,) \n",
      "[0 1 2 3 4]\n",
      "\n",
      "np.outer(a, b)  Shape:(3, 5) \n",
      "[[0 0 0 0 0]\n",
      " [0 1 2 3 4]\n",
      " [0 2 4 6 8]]\n"
     ]
    }
   ],
   "source": [
    "###############################################\n",
    "# outer 外积 只按照一维数组进行计算  如果传入是多维数据，则将数组 ravel 为一维数组\n",
    "a =  np.arange(3)\n",
    "b =  np.arange(5)\n",
    "print('\\n{} Shape:{} \\n{}'.format('a', a.shape, a))\n",
    "print('\\n{} Shape:{} \\n{}'.format('b', b.shape, b))\n",
    "print('\\n{} Shape:{} \\n{}'.format('np.outer(a, b) ', (np.outer(a, b)).shape, np.outer(a, b)))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a shape：(3, 1):\n",
      "[[1]\n",
      " [1]\n",
      " [1]]\n",
      "\n",
      "b shape：(1, 5):\n",
      "[[3 3 3 3 3]]\n",
      "\n",
      "c shape：(3, 5):\n",
      "[[0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 0.]]\n",
      "\n",
      "\n",
      "np.expand_dims(a, axis=0) shape：(1, 3, 1):\n",
      "[[[1]\n",
      "  [1]\n",
      "  [1]]]\n",
      "\n",
      "\n",
      "np.expand_dims(a, axis=-1) shape：(3, 1, 1):\n",
      "[[[1]]\n",
      "\n",
      " [[1]]\n",
      "\n",
      " [[1]]]\n",
      "\n",
      "np.hstack((a,c)：\n",
      "[[1. 0. 0. 0. 0. 0.]\n",
      " [1. 0. 0. 0. 0. 0.]\n",
      " [1. 0. 0. 0. 0. 0.]]\n",
      "\n",
      "np.vstack((b,c)：\n",
      "[[3. 3. 3. 3. 3.]\n",
      " [0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 0.]]\n",
      "\n",
      "\n",
      "a shape：(2, 3, 5):\n",
      "[[[ 0  1  2  3  4]\n",
      "  [ 5  6  7  8  9]\n",
      "  [10 11 12 13 14]]\n",
      "\n",
      " [[15 16 17 18 19]\n",
      "  [20 21 22 23 24]\n",
      "  [25 26 27 28 29]]]\n",
      "\n",
      "\n",
      "np.transpose(a, (0,2,1)) shape：(2, 5, 3):\n",
      "[[[ 0  5 10]\n",
      "  [ 1  6 11]\n",
      "  [ 2  7 12]\n",
      "  [ 3  8 13]\n",
      "  [ 4  9 14]]\n",
      "\n",
      " [[15 20 25]\n",
      "  [16 21 26]\n",
      "  [17 22 27]\n",
      "  [18 23 28]\n",
      "  [19 24 29]]]\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# 多个 多维数组操作\n",
    "a = np.full(shape=(3,1), fill_value=1)\n",
    "b = np.full(shape=(1,5), fill_value=3)\n",
    "c = np.zeros((3,5))\n",
    "\n",
    "print('a shape：{}:\\n{}\\n'.format(a.shape, a))\n",
    "print('b shape：{}:\\n{}\\n'.format(b.shape, b))\n",
    "print('c shape：{}:\\n{}\\n'.format(c.shape, c))\n",
    "\n",
    "# expand_dims  维度扩展\n",
    "print('\\n{} shape：{}:\\n{}\\n'.format('np.expand_dims(a, axis=0)', np.expand_dims(a, axis=0).shape, np.expand_dims(a, axis=0)))\n",
    "print('\\n{} shape：{}:\\n{}\\n'.format('np.expand_dims(a, axis=-1)', np.expand_dims(a, axis=-1).shape, np.expand_dims(a, axis=-1)))\n",
    "\n",
    "print('np.hstack((a,c)：\\n{}\\n'.format(np.hstack((a,c))))  # np.concatenate((a, c), axis=1)\n",
    "print('np.vstack((b,c)：\\n{}\\n'.format(np.vstack((b,c))))  # np.concatenate((b, c), axis=0)\n",
    "\n",
    "# transpose  重新设置轴的顺序\n",
    "a = np.arange(30).reshape((2,3,5))\n",
    "print('\\na shape：{}:\\n{}\\n'.format(a.shape, a))\n",
    "print('\\n{} shape：{}:\\n{}\\n'.format('np.transpose(a, (0,2,1))', np.transpose(a, (0,2,1)).shape, np.transpose(a, (0,2,1))))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## 掩码数组\n",
    "一个掩码数组由一个正常数组和一个布尔数组组成。 几乎完整复制 NumPy 提供的所有函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "原始数据：\n",
      "[[5 0 3 3 7]\n",
      " [9 3 5 2 4]\n",
      " [7 6 8 8 1]\n",
      " [6 7 7 8 1]]\n",
      "\n",
      "掩码数据：\n",
      "[[False False False False  True]\n",
      " [ True False False False False]\n",
      " [ True  True  True  True False]\n",
      " [ True  True  True  True False]]\n",
      "\n",
      "填充数据：\n",
      "8888\n",
      "\n",
      "\n",
      "count：\n",
      "[1 2 2 2 3]\n"
     ]
    }
   ],
   "source": [
    "np.random.seed(0)\n",
    "# 原始数组\n",
    "a = np.random.randint(0, 10, size=(4,5))\n",
    "# 掩码部分\n",
    "mask = a >5\n",
    "a_mask = np.ma.array(a, mask=mask, fill_value=8888)\n",
    "\n",
    "print('原始数据：\\n{}'.format(a_mask.data))\n",
    "print('\\n掩码数据：\\n{}'.format(a_mask.mask))\n",
    "print('\\n填充数据：\\n{}'.format(a_mask.fill_value))\n",
    "print('\\n\\ncount：\\n{}'.format(a_mask.count(axis=0)))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## 数组保存"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CSV文件读取:\n",
      " [[5. 0. 3. 3. 7.]\n",
      " [9. 3. 5. 2. 4.]\n",
      " [7. 6. 8. 8. 1.]\n",
      " [6. 7. 7. 8. 1.]]\n",
      "\n",
      "npy文件读取:\n",
      " [[5 0 3 3 7]\n",
      " [9 3 5 2 4]\n",
      " [7 6 8 8 1]\n",
      " [6 7 7 8 1]]\n"
     ]
    }
   ],
   "source": [
    "import os \n",
    "os.makedirs('./_temp', exist_ok=True)\n",
    "\n",
    "# 数组保存\n",
    "np.savetxt('./_temp/test.csv', a, fmt='%d', delimiter=',')\n",
    "np.save('./_temp/test.npy', a)\n",
    "\n",
    "# 文件读取\n",
    "print('CSV文件读取:\\n', np.loadtxt('./_temp/test.csv', delimiter=','))\n",
    "print('\\nnpy文件读取:\\n', np.load('./_temp/test.npy'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## 相似度计算"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 根据词向量计算词之间的余弦相似度矩阵  【衡量两个向量方向的差异】\n",
    "def cos_similar(np_a, np_b):\n",
    "    \"\"\"\n",
    "    :param np_a:  词向量 np 格式\n",
    "    :param np_b:  词向量 np 格式\n",
    "    :return:  相似度矩阵 词数量*词数量\n",
    "    \"\"\"\n",
    "    # 若 np_a、 np_b 为一维向量，将其 reshape 为 2维向量\n",
    "    if len(np_a.shape) == 1:\n",
    "        np_a = np_a.reshape(1, -1)\n",
    "    if len(np_b.shape) == 1:\n",
    "        np_b = np_b.reshape(1, -1)\n",
    "\n",
    "    # 计算 np_a 的 2范数\n",
    "    a_norm = np.linalg.norm(np_a, axis=1)\n",
    "    a_norm = a_norm.reshape(-1, 1)\n",
    "\n",
    "    # 计算 np_b 的 2范数\n",
    "    b_norm = np.linalg.norm(np_b, axis=1)\n",
    "    b_norm = b_norm.reshape(1, -1)\n",
    "\n",
    "    # 余弦相似度 【防止分母为 0 输出为Nan 加上一个极小数 1e-8 】\n",
    "    cos_similar_score = np_a.dot(np_b.T) / (a_norm.dot(b_norm)+1e-8)\n",
    "\n",
    "    return cos_similar_score\n",
    "\n",
    "\n",
    "# 欧氏距离\n",
    "def euclidean_similar(np_a, np_b):\n",
    "    # 若 np_a、 np_b 为一维向量，将其 reshape 为 2维向量\n",
    "    if len(np_a.shape) == 1:\n",
    "        np_a = np_a.reshape(1, -1)\n",
    "    if len(np_b.shape) == 1:\n",
    "        np_b = np_b.reshape(1, -1)\n",
    "\n",
    "    # 若 np_a 的长度大于 np_b时，a,b 互换，减少循环次数提高效率\n",
    "    if np_a.shape[0] > np_b.shape[0]:\n",
    "        np_a, np_b = np_b, np_a\n",
    "        change_flag = True\n",
    "    else:\n",
    "        change_flag = False\n",
    "\n",
    "    # 欧氏距离 Euclidean \n",
    "    euclidean_similar_m = np.zeros([len(np_a), len(np_b)])\n",
    "\n",
    "    for np_i, np_a_i in enumerate(np_a):\n",
    "        euclidean_similar_m[np_i, :] = np.sqrt(((np_a_i-np_b)**2).sum(axis=1))\n",
    "    \n",
    "    # 若前面 a,b 互换，则相似系数矩阵转置\n",
    "    if change_flag:\n",
    "        euclidean_similar_m = euclidean_similar_m.T\n",
    "\n",
    "    return euclidean_similar_m\n",
    "\n",
    "\n",
    "# 杰卡德相似系数 【杰卡德相似系数是衡量两个集合的相似度一种指标】\n",
    "def jaccard_similar(np_a, np_b):\n",
    "    \"\"\"\n",
    "    :param np_a:  词向量 np 格式   为保证计算速度，长度小的数据尽量放在 np_a 位置\n",
    "    :param np_b:  词向量 np 格式\n",
    "    :return:  相似度矩阵 词数量*词数量\n",
    "    \"\"\"\n",
    "    # 若 np_a、 np_b 为一维向量，将其 reshape 为 2维向量\n",
    "    if len(np_a.shape) == 1:\n",
    "        np_a = np_a.reshape(1, -1)\n",
    "    if len(np_b.shape) == 1:\n",
    "        np_b = np_b.reshape(1, -1)\n",
    "\n",
    "    np_a = np.where(np_a > 0, np.ones_like(np_a), np.zeros_like(np_a))  # 将大于0的设置为1，其余设置为0\n",
    "    np_b = np.where(np_b > 0, np.ones_like(np_b), np.zeros_like(np_b))  # 将大于0的设置为1，其余设置为0\n",
    "\n",
    "    # 若 np_a 的长度大于 np_b时，a,b 互换，减少循环次数提高效率\n",
    "    if np_a.shape[0] > np_b.shape[0]:\n",
    "        np_a, np_b = np_b, np_a\n",
    "        change_flag = True\n",
    "    else:\n",
    "        change_flag = False\n",
    "\n",
    "    # a, b 的交集\n",
    "    ab_intersection = np.dot(np_a, np_b.T)\n",
    "\n",
    "    # a, b 各自的数量\n",
    "    np_a_sum = np_a.sum(axis=1)\n",
    "    np_b_sum = np_b.sum(axis=1)\n",
    "\n",
    "    jaccard_similar_m = np.zeros([len(np_a), len(np_b)])\n",
    "\n",
    "    #  a, b 并集+交集 数量\n",
    "    for np_i, np_a_i in enumerate(np_a_sum):\n",
    "        jaccard_similar_m[np_i, :] = np_a_i + np_b_sum\n",
    "\n",
    "    #  杰卡德相似系数 = 交集/并集\n",
    "    jaccard_similar_m = ab_intersection/(jaccard_similar_m - ab_intersection)\n",
    "\n",
    "    # 若前面 a,b 互换，则相似系数矩阵转置\n",
    "    if change_flag:\n",
    "        jaccard_similar_m = jaccard_similar_m.T\n",
    "\n",
    "    return jaccard_similar_m\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "余弦相似度:\n",
      " [[0.   1.  ]\n",
      " [0.71 0.71]\n",
      " [0.71 0.71]]\n",
      "欧氏距离:\n",
      " [[1.41 0.  ]\n",
      " [1.   1.  ]\n",
      " [1.   1.  ]]\n",
      "杰卡德相似度:\n",
      " [[0.  1. ]\n",
      " [0.5 0.5]\n",
      " [0.5 0.5]]\n"
     ]
    }
   ],
   "source": [
    "a = np.array([[1,0], [1,1], [1,1]])\n",
    "b = np.array([[0,1], [1,0]])\n",
    "\n",
    "print('余弦相似度:\\n', cos_similar(a, b))\n",
    "print('欧氏距离:\\n', euclidean_similar(a, b))\n",
    "print('杰卡德相似度:\\n', jaccard_similar(a, b))\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
